6.8 NOI 模拟

2022-10-19,,

\(T1\ edge\)

考虑\(O(q\times n\times \log n)\)的暴力

暴力二分,直接树上差分

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define MAXN 50005
using namespace std;
int head[MAXN],nxt[MAXN<<1],to[MAXN<<1],tot;
int dep[MAXN],tag[MAXN],val[MAXN],f[MAXN][22],n,m,q;
struct node
{
int x,y;
bool s;
}li[MAXN];
void add(int u,int v)
{
tot++;
to[tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
void dfs_pre(int now,int fa)
{
f[now][0]=fa;
dep[now]=dep[fa]+1;
for(int i=1;i<=20;i++)
{
f[now][i]=f[f[now][i-1]][i-1];
}
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
dfs_pre(y,now);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int dfs(int now,int fa)
{
int res=0;
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
res+=dfs(y,now);
}
res+=tag[now];
val[now]=res;
return res;
}
int lca[3005][3005];
bool check(int x,int r)
{
memset(val,0,sizeof(val));
memset(tag,0,sizeof(tag));
for(int i=x;i<=r;i++)
{
if(!li[i].s) continue;
int u=li[i].x,v=li[i].y;
tag[u]+=1; tag[v]+=1;
tag[lca[u][v]]+=-1;
tag[f[lca[u][v]][0]]+=-1;
}
dfs(1,1);
for(int i=1;i<=n;i++)
{
if(!val[i]) return false;
}
return true;
}
int sol(int num)
{
int l=1,r=num,mid,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid,num))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1,u,v;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
dfs_pre(1,0);
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
lca[i][j]=lca[j][i]=LCA(i,j);
}
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&li[i].x,&li[i].y);
}
for(int i=1,r,p,opt;i<=q;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d",&p);
li[p].s=true;
}
else
{
scanf("%d",&r);
cout<<sol(r)<<"\n";
}
}
}

这样是没有前途的

观察数据范围,发现大概可能是个\(O(n\sqrt n \log n)\)

考虑这样一个暴力,从前往后扫,树剖维护链覆盖,取全局最小值即可

考虑区间分块,每个块尾维护一个前缀的树剖,空间复杂度\(O(n\sqrt n)\)

每次修改的话就是,把后面的块都暴力修改一遍,单次复杂度\(O(\sqrt n \log n)\)

查询的话就是找最近的一个块往后移动,查询全局最小值即可,然后\(vector\)撤回操作,单次复杂度\(O(\sqrt n \log n)\)

根据常数大小获得\(60pts\sim 100pts\)

代码,\(std\)的

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
const int Maxn=50000;
const int Maxq=100000;
const int Maxb=1500;
const int Maxv=(Maxn-1)/Maxb+1;
const int Inf=0x3f3f3f3f;
int n,m,q;
struct Edge{
int u,v;
}edge[Maxn+5];
bool vis_op[Maxn+5];
int op_id[Maxq+5];
int ans[Maxq+5];
int find_belong(int x){
return (x-1)/Maxb+1;
}
int find_bel_l(int x){
return (x-1)*Maxb+1;
}
int find_bel_r(int x){
return std::min(m,x*Maxb);
}
int head[Maxn+5],arrive[Maxn<<1|5],nxt[Maxn<<1|5],tot;
void add_edge(int from,int to){
arrive[++tot]=to;
nxt[tot]=head[from];
head[from]=tot;
}
int fa[Maxn+5],sz[Maxn+5],son[Maxn+5],dep[Maxn+5],top[Maxn+5];
int dfn[Maxn+5],rnk[Maxn+5],dfn_tot;
void init_dfs_1(int u){
sz[u]=1;
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
fa[v]=u;
init_dfs_1(v);
sz[u]+=sz[v];
if(son[u]==0||sz[v]>sz[son[u]]){
son[u]=v;
}
}
}
void init_dfs_2(int u,int tp){
dfn[u]=++dfn_tot;
rnk[dfn[u]]=u;
top[u]=tp;
if(son[u]){
init_dfs_2(son[u],tp);
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]||v==son[u]){
continue;
}
init_dfs_2(v,v);
}
}
int find_lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
std::swap(u,v);
}
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
bool vis[Maxn+5];
int vis_up[Maxn+5],vis_down[Maxn+5];
bool on_path[Maxn+5];
void vir_dfs_1(int u){
if(vis[u]){
vis_up[u]=vis_down[u]=u;
}
else{
vis_up[u]=vis_up[fa[u]];
}
int num_son=0;
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
vir_dfs_1(v);
if(vis_down[v]&&!vis[u]){
num_son++;
vis_down[u]=vis_down[v];
}
}
if(!vis[u]&&num_son>1){
vis[u]=1;
vis_up[u]=vis_down[u]=u;
}
}
void vir_dfs_2(int u){
if(vis_up[u]&&vis_down[u]){
on_path[u]=1;
if(!vis[u]){
vis_up[u]=vis_up[fa[u]];
}
}
else{
if(on_path[fa[u]]){
vis_up[u]=fa[u];
}
else{
vis_up[u]=vis_up[fa[u]];
}
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
vir_dfs_2(v);
}
}
namespace Subtask_1{
int val_num[Maxn+5],val_pos;
struct Node{
int ch[2];
int dep_l,dep_r;
int cur_val,mn_val,lazy;
}node[Maxn+5];
int lis[Maxn+5],sum[Maxn+5],root[Maxn+5],mn_n[Maxn+5];
int len;
int build_line(int l,int r){
if(l>r){
return 0;
}
int left=l,right=r;
while(left<right){
int mid=(left+right)/2;
if(sum[mid]-sum[l-1]<sum[r]-sum[mid]){
left=mid+1;
}
else{
right=mid;
}
}
int root=lis[left];
node[root].dep_l=dep[lis[l]],node[root].dep_r=dep[lis[r]];
node[root].ch[0]=build_line(l,left-1);
node[root].ch[1]=build_line(left+1,r);
node[root].cur_val=node[root].mn_val=node[root].lazy=0;
return root;
}
void build_tree(int u){
int lst_fa=fa[u];
fa[u]=0;
auto get_line=[&](int x){
len=0;
while(fa[x]&&son[fa[x]]==x){
lis[++len]=x;
x=fa[x];
}
lis[++len]=x;
std::reverse(lis+1,lis+1+len);
for(int i=1;i<=len;i++){
sum[i]=sum[i-1]+sz[lis[i]]-sz[son[lis[i]]];
}
int r=build_line(1,len);
for(int i=1;i<=len;i++){
root[lis[i]]=r;
mn_n[lis[i]]=x;
}
val_num[node[r].mn_val]++;
};
for(int i=dfn[u];i<dfn[u]+sz[u];i++){
int v=rnk[i];
if(son[v]==0){
get_line(v);
}
}
fa[u]=lst_fa;
}
void update_tag(int root,int a){
node[root].cur_val=std::max(node[root].cur_val,a),node[root].mn_val=std::max(node[root].mn_val,a),node[root].lazy=std::max(node[root].lazy,a);
}
void push_up(int root){
node[root].mn_val=std::max(std::min(node[root].cur_val,std::min(node[node[root].ch[0]].mn_val,node[node[root].ch[1]].mn_val)),node[root].lazy);
}
void _update(int root,int l,int r,int val){
if(root==0||r<node[root].dep_l||l>node[root].dep_r||l>r||val<=node[root].lazy){
return;
}
if(l<=node[root].dep_l&&r>=node[root].dep_r){
update_tag(root,val);
return;
}
if(l<=dep[root]&&r>=dep[root]){
node[root].cur_val=std::max(node[root].cur_val,val);
}
_update(node[root].ch[0],l,r,val),_update(node[root].ch[1],l,r,val);
push_up(root);
}
void update(int root,int l,int r,int val){
val_num[node[root].mn_val]--;
_update(root,l,r,val);
val_num[node[root].mn_val]++;
while(val_pos<=m&&val_num[val_pos]==0){
val_pos++;
}
}
void modify_root(int u,int val){
while(mn_n[u]){
update(root[u],dep[mn_n[u]],dep[u],val);
u=fa[mn_n[u]];
}
}
void modify_path(int u,int v,int val){
while(mn_n[u]!=mn_n[v]){
if(dep[mn_n[u]]<dep[mn_n[v]]){
std::swap(u,v);
}
update(root[u],dep[mn_n[u]],dep[u],val);
u=fa[mn_n[u]];
}
if(dep[u]>dep[v]){
std::swap(u,v);
}
update(root[u],dep[u]+1,dep[v],val);
}
void init_dfs(int u){
if(vis_down[u]==0){
build_tree(u);
return;
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
init_dfs(v);
}
}
void init(){
for(int i=1;i<=n;i++){
mn_n[i]=0;
}
node[0].mn_val=Inf;
val_pos=0;
for(int i=0;i<=m;i++){
val_num[i]=0;
}
init_dfs(1);
while(val_pos<=m&&val_num[val_pos]==0){
val_pos++;
}
}
}
namespace Subtask_2{
struct Segment_Tree{
int n;
std::vector<int> val;
void init(int _n){
n=_n;
val.clear();
val.resize(n*2+2);
val[n*2]=Inf;
}
void update(int l,int r,int a){
if(l>r){
return;
}
int s_l=l+n-1,s_r=r+n-1;
for(l+=n-1,r+=n;l<r;l>>=1,r>>=1){
if(l&1){
val[l]=std::max(val[l],a);
l++;
}
if(r&1){
r--;
val[r]=std::max(val[r],a);
}
val[l>>1]=std::max(val[l>>1],std::min(val[l],val[l^1]));
val[r>>1]=std::max(val[r>>1],std::min(val[r],val[r^1]));
}
for(;s_l>1;s_l>>=1){
val[s_l>>1]=std::max(val[s_l>>1],std::min(val[s_l],val[s_l^1]));
}
for(;s_r>1;s_r>>=1){
val[s_r>>1]=std::max(val[s_r>>1],std::min(val[s_r],val[s_r^1]));
}
}
int query(){
int ans=Inf;
for(int l=n,r=n*2;l<r;l>>=1,r>>=1){
if(l&1){
ans=std::min(ans,val[l++]);
}
if(r&1){
ans=std::min(ans,val[--r]);
}
}
return ans;
}
}seg[Maxb<<1|5];
int seg_id[Maxn+5];
int seg_id_tot;
void init_dfs(int u){
if(vis_down[u]==0){
return;
}
if(vis[u]&&fa[u]){
seg_id[u]=++seg_id_tot;
seg[seg_id[u]].init(dep[u]-dep[vis_up[fa[u]]]);
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
init_dfs(v);
}
}
void init(){
seg_id_tot=0;
init_dfs(1);
}
void modify_path(int u,int v,int val){
if(dep[u]>dep[v]){
std::swap(u,v);
}
int pos=vis_down[v];
seg[seg_id[pos]].update(dep[u]-dep[vis_up[fa[pos]]]+1,dep[v]-dep[vis_up[fa[pos]]],val);
}
int query_path(int u){
return seg[seg_id[vis_down[u]]].query();
}
}
namespace Subtask_3{
struct Node{
int ch[2];
int dep_l,dep_r;
int cur_val,lazy;
}node[Maxb<<1|5];
int sz[Maxb<<1|5],fa[Maxb<<1|5],dep[Maxb<<1|5],son[Maxb<<1|5],top[Maxb<<1|5],mn_r[Maxb<<1|5];
std::vector<int> g[Maxb<<1|5];
int re_id[Maxn+5],re_id_tot;
void init_dfs_1(int u){
dep[u]=dep[fa[u]]+1;
sz[u]=1;
son[u]=0;
for(int v:g[u]){
fa[v]=u;
init_dfs_1(v);
sz[u]+=sz[v];
if(son[u]==0||sz[v]>sz[son[u]]){
son[u]=v;
}
}
}
int lis[Maxb<<1|5],len;
int sum_sz[Maxb<<1|5];
int build_line(int l,int r){
if(l>r){
return 0;
}
int left=l,right=r;
while(left<right){
int mid=(left+right)/2;
if(sum_sz[mid]-sum_sz[l-1]<sum_sz[r]-sum_sz[mid]){
left=mid+1;
}
else{
right=mid;
}
}
int root=lis[left];
node[root].dep_l=dep[lis[l]],node[root].dep_r=dep[lis[r]];
node[root].ch[0]=build_line(l,left-1);
node[root].ch[1]=build_line(left+1,r);
node[root].cur_val=node[root].lazy=0;
return root;
}
void build_tree(){
for(int i=1;i<=len;i++){
sum_sz[i]=sum_sz[i-1]+sz[lis[i]]-sz[son[lis[i]]];
}
int root=build_line(1,len);
for(int i=1;i<=len;i++){
mn_r[lis[i]]=root;
}
}
void init_dfs_2(int u,int tp){
top[u]=tp;
lis[++len]=u;
if(son[u]){
init_dfs_2(son[u],tp);
}
else{
build_tree();
len=0;
}
for(int v:g[u]){
if(v==son[u]){
continue;
}
init_dfs_2(v,v);
}
}
void update_tag(int root,int a){
node[root].cur_val=std::max(node[root].cur_val,a),node[root].lazy=std::max(node[root].lazy,a);
}
void update(int root,int l,int r,int val){
if(root==0||r<node[root].dep_l||l>node[root].dep_r||val<=node[root].lazy){
return;
}
if(l<=node[root].dep_l&&r>=node[root].dep_r){
update_tag(root,val);
return;
}
if(l<=dep[root]&&r>=dep[root]){
node[root].cur_val=std::max(node[root].cur_val,val);
}
update(node[root].ch[0],l,r,val),update(node[root].ch[1],l,r,val);
}
void modify_path(int u,int v,int val){
int num=0;
u=re_id[u],v=re_id[v];
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
std::swap(u,v);
}
num++;
update(mn_r[u],dep[top[u]],dep[u],val);
u=fa[top[u]];
}
if(dep[u]>dep[v]){
std::swap(u,v);
}
num++;
update(mn_r[u],dep[u]+1,dep[v],val);
// fprintf(stderr,"%d %d\n",re_id_tot,num);
}
void _push_down(int root,int val=0){
if(root==0){
return;
}
val=std::max(val,node[root].lazy);
node[root].lazy=val;
node[root].cur_val=std::max(node[root].cur_val,val);
_push_down(node[root].ch[0],val),_push_down(node[root].ch[1],val);
}
void push_down(){
for(int i=1;i<=re_id_tot;i++){
if(top[i]==i){
_push_down(mn_r[i]);
}
}
}
void init(){
for(int i=1;i<=re_id_tot;i++){
fa[i]=0,g[i].clear();
}
re_id_tot=0;
for(int i=1;i<=n;i++){
if(vis[i]){
re_id[i]=++re_id_tot;
}
}
for(int i=1;i<=n;i++){
if(vis[i]){
if(::fa[i]){
g[re_id[vis_up[::fa[i]]]].push_back(re_id[i]);
}
}
}
init_dfs_1(1);
init_dfs_2(1,1);
}
namespace DSU{
int num;
int fa[Maxb<<1|5];
void init(){
num=re_id_tot;
for(int i=1;i<=re_id_tot;i++){
fa[i]=i;
}
}
int find(int x){
if(x==fa[x]){
return x;
}
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fa_x=find(x),fa_y=find(y);
if(fa_x==fa_y){
return;
}
fa[fa_y]=fa_x;
num--;
}
}
void merge_path(int u,int v){
while(u!=v){
if(dep[u]<dep[v]){
std::swap(u,v);
}
DSU::merge(fa[u],u);
u=DSU::find(u);
}
}
int query(int l,int r){
DSU::init();
for(int i=r;i>=l;i--){
if(!vis_op[i]){
continue;
}
int cur_u=re_id[edge[i].u],cur_v=re_id[edge[i].v];
merge_path(cur_u,cur_v);
if(DSU::num==1){
return i;
}
}
push_down();
int ans=Inf;
for(int i=2;i<=re_id_tot;i++){
if(DSU::find(i)==i){
ans=std::min(ans,node[i].cur_val);
}
}
return ans;
}
}
void change_path(int u,int v,int val){
if(u==v){
return;
}
Subtask_2::modify_path(u,v,val);
Subtask_3::modify_path(vis_up[fa[v]],vis_down[v],Subtask_2::query_path(v));
}
void _update_path(int u,int fa,int val){
if(u==fa){
return;
}
if(!on_path[fa]){
Subtask_1::modify_path(u,fa,val);
}
else if(on_path[u]){
if(vis_up[::fa[u]]==vis_up[fa]){
change_path(fa,u,val);
}
else{
change_path(fa,vis_down[fa],val);
change_path(vis_up[::fa[u]],u,val);
Subtask_3::modify_path(vis_down[fa],vis_up[::fa[u]],val);
}
}
else{
Subtask_1::modify_root(u,val);
u=vis_up[u];
_update_path(u,fa,val);
}
}
void update_path(int u,int v,int val){
int lca=find_lca(u,v);
_update_path(u,lca,val),_update_path(v,lca,val);
}
int query(int bel_l,int r){
return std::min(Subtask_1::val_pos,Subtask_3::query(bel_l,r));
}
namespace Init{
int head[Maxn+5],arrive[Maxn<<1|5],nxt[Maxn<<1|5],tot;
void add_edge(int from,int to){
arrive[++tot]=to;
nxt[tot]=head[from];
head[from]=tot;
}
int dfn[Maxn+5],dfn_tot;
void init_dfs(int u,int fa){
dfn[u]=++dfn_tot;
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa){
continue;
}
init_dfs(v,u);
::add_edge(dfn[u],dfn[v]);
}
}
void init(){
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v),add_edge(v,u);
}
init_dfs(1,0);
}
}
int qry_num[Maxn+5];
int main(){ scanf("%d%d%d",&n,&m,&q);
Init::init();
/*for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v),add_edge(v,u);
}*/
for(int i=1;i<=m;i++){
scanf("%d%d",&edge[i].u,&edge[i].v);
edge[i].u=Init::dfn[edge[i].u],edge[i].v=Init::dfn[edge[i].v];
}
for(int i=1;i<=q;i++){
int op;
scanf("%d%d",&op,&op_id[i]);
if(op==2){
qry_num[op_id[i]]++;
op_id[i]=-op_id[i];
}
}
init_dfs_1(1);
init_dfs_2(1,1);
for(int l=1,r;l<=m;l=r){
r=l;
memset(vis_up,0,sizeof vis_up),memset(vis_down,0,sizeof vis_down),memset(vis,0,sizeof vis),memset(on_path,0,sizeof on_path),memset(vis_op,0,sizeof vis_op);
vis[1]=1;
int num=1;
while(r<=m){
num+=(!vis[edge[r].u])+(!vis[edge[r].v])+qry_num[r];
vis[edge[r].u]=vis[edge[r].v]=1;
r++;
if(num>Maxb){
break;
}
}
vir_dfs_1(1),vir_dfs_2(1);
Subtask_1::init();
Subtask_2::init();
Subtask_3::init();
std::vector<int> cur_q;
for(int j=1;j<=q;j++){
if(op_id[j]<0){
std::sort(cur_q.begin(),cur_q.end(),[](int p,int q){return p>q;});
for(int k:cur_q){
update_path(edge[k].u,edge[k].v,k);
}
cur_q.clear();
if(l<=-op_id[j]&&-op_id[j]<=r){
ans[j]=query(l,-op_id[j]);
}
}
else{
if(op_id[j]<l){
cur_q.push_back(op_id[j]);
}
vis_op[op_id[j]]=1;
}
}
}
for(int i=1;i<=q;i++){
if(op_id[i]<0){
printf("%d\n",ans[i]);
}
}
return 0;
}

\(T2\ zero\)

我们可以推出来第一步

考虑一下生成函数

\[F_k=F_{k-1}+x F_k + x^2F_k
\\
(1-x-x^2)F_k=F_{k-1}
\\
F_k=\frac{F_{k-1}}{1-x-x^2}
\\
F_k=\frac{F_1}{(1-x-x^2)^{k-1}}
\\
F_k=\frac{1}{(1-x-x^2)^{k}}
\]

\(F_0\)是原始的斐波那契数列

我们要求\(F_k\)的第\(n\)项,\(n<=1\times 10^{100000}\)

这个暴力多项式计算的话,会寄,可以拿到\(1pts\)

这个常系数线性齐次递推的话,会寄,可以拿到\(1pts\)

这个暴力递推的话,不会寄。。。

考虑继续推式子

\[[x^n]\frac{1}{(1-x-x^2)^k}
\\
\lambda_1=\frac{1+\sqrt 5}{2}\ \ \ \ \ \lambda_2=\frac{1-\sqrt 5}{2}
\\
[x^n]\frac{1}{(1-\lambda_1 x)^k}\frac{1}{(1-\lambda_2 x)^k}
\]

考虑初始形态

\[\frac{1}{1-x}=1+x+x^2+x^3...
\\
[x^n]\frac{1}{(1-x)^k}=\binom{n+k-1}{k-1}
\]

首先设

\[K=k-1
\\
q=\frac{\lambda_1}{\lambda_2}
\\
C=\frac{\lambda_2^n (-1)^K}{(K!)^2}
\]

然后

\[\sum_{i=0}^n\binom{i+K}{K}\binom{n-i+K}{K}\lambda_1^i\lambda_2^{n-i}
\\
\frac{\lambda_2^n}{(K!)^2}\sum_{i=0}^n(i+K)^{\underline{K}}(n-i+K)^{\underline{K}}q^i
\\
C\sum_{i=0}^n (i+K)^{\underline{K}}(i-n-1)^{\underline{K}}q^i
\\
C\sum_{i=0}^n(i+K)^{\underline{K}}q^i\sum_{j=0}^K\binom{K}{j} i^j(-n-1)^{K-j}
\\
C\sum_{j=0}^K\binom{K}{j}(-n-1)^{\underline{K-j}}\sum_{i=0}^n(i+K)^{\underline{K+j}}q^i
\]

前半部分可以做到\(O(k)\),考虑后半部分如何线性预处理

\[S_p=\sum_{i=0}^n(i+K)^{\underline{p}}q^i
\\
\sum_{i=0}^n (i+K-1)^{\underline{p}}q^i+p(i+K-1)^{\underline{p-1}}q^i
\\
q\times S_p-q^{n+1}(n+K)^{\underline{p}}+(K-1)^\underline{p}+p(q\times S_{p-1}-q^{n+1}(n+K)^{\underline{p-1}}+(K-1)^\underline{p-1})
\]

也可以\(O(k)\)得到

#include<bits/stdc++.h>
#define N 10000000
#define int long long
#define mode 469762049
using namespace std;
int poww(int x,int y)
{
int su=1;
while(y)
{
if(y&1)
{
su=su*x%mode;
}
x=x*x%mode;
y>>=1;
}
return su;
}
const int lam1=poww(6922763,mode-2),lam2=poww(462839285,mode-2),q=lam1*poww(lam2,mode-2)%mode,inq=poww(q,mode-2);
int inv(int x)
{
return poww(x,mode-2);
}
int k,n,nn,jc[N+10],invs[N+10],f[N*2+10];
char s[N];
signed main()
{
jc[0]=1;
for(int i=1;i<=N;++i) jc[i]=jc[i-1]*i%mode;
invs[N]=inv(jc[N]);
for(int i=N-1;i>=0;--i) invs[i]=invs[i+1]*(i+1)%mode;
cin>>k>>s+1;
int l=strlen(s+1);
for(int i=1;i<=l;++i)
{
n=(n*10+s[i]-'0')%mode;
nn=(nn*10+s[i]-'0')%(mode-1);
}
int f1=mode-poww(q,nn+1),f2=1,v1=(n+k-1)%mode,v2=(k-2+mode)%mode,iv=inv(mode+1-q);
f[0]=iv*(f1+1)%mode;
for(int i=1;i<=2*k;++i)
{
f[i]=i*((q*f[i-1]%mode+f1+f2)%mode)%mode;
f1=f1*(v1--)%mode;
f2=f2*(v2--)%mode;
f[i]=(f[i]+f1+f2)*iv%mode;
}
int cf=poww(lam2,nn)*invs[k-1]%mode*invs[k-1]%mode;
if(k%2==0) cf=(mode-cf)%mode;
int ans=0,v=mode-n-1,ff=1;
for(int i=k-1;i>=0;--i)
{
int p=jc[k-1]*invs[i]%mode*invs[k-1-i]%mode*ff%mode;
ans=(ans+p*f[k-1+i]%mode)%mode;
ff=ff*(v--)%mode;
}
ans=ans*cf%mode;
cout<<ans<<endl;
}

\(T3\ diana\)

首先容易想到式子

\[\varphi(a\times b)=\frac{\varphi(a)\varphi(b)\gcd(a,b)}{\varphi(\gcd(a,b))}
\]

维护区间最大值,由于后面的\(\frac{\gcd(a,b)}{\varphi(gcd(a,b))}\)不好处理,那么就枚举这一部分

我们枚举\(\gcd\),拿出所有\(\gcd\)的倍数,按照\(\frac{\varphi(a)\varphi(b)\varphi(\gcd)}{\gcd}\)去计算,这样算即使部分不是正确的,但算出的结果会\(\leq\)真实结果

我们可以暴力枚举\(\gcd\),暴力统计,复杂度\(O(q\times n\times \ln (n))\)

考虑 \(q\) 很大而 \(n\) 很小,我们枚举 \(\gcd\) 记三元组 \((i,j,val)\),然后扫描线一下即可

这个时候的问题是,\(n\) 大的时候,三元组个数会很多

当 \(\gcd\) 确定时大小只和 \(\varphi(p_i)\) 和 \(\varphi(p_j)\) 有关,考虑枚举两者之间的较小值

结论:答案只可能是距离 \(i\) 最近的并且 \(\varphi(p_j)>\varphi(p_i)\) 的左右两值

证明:考虑\(\varphi(p_k)>\varphi(p_j)>\varphi(p_i),k<j<i\),\(i\) 和 \(k\) 匹配一定不比 \(j\) 和 \(k\) 匹配更优

然后现在三元组数量控制在 \(O(n\ln n)\)就可以愉快扫描线了

\(45pts\)线段树惨遭卡常代码

#include<bits/stdc++.h>
#define int long long
#define MAXN 500005
using namespace std;
struct node
{
int l,r,id,val;
}li[MAXN<<5];
int Ans[MAXN];
template<class T>
T Read()
{
T x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int (*read)()=Read<int>;
#define read Read<int>
namespace Seg
{
#define ls (now<<1)
#define rs ((now<<1)|1)
struct Tree
{
int l,r,Max;
}tr[MAXN<<4];
void build(int now,int l,int r)
{
tr[now].l=l;
tr[now].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void push_up(int now)
{
tr[now].Max=max(tr[ls].Max,tr[rs].Max);
}
void change(int now,int poz,int k)
{
if(tr[now].l==poz&&tr[now].r==poz)
{
tr[now].Max=max(tr[now].Max,k);
return;
}
int mid=(tr[now].l+tr[now].r)>>1;
if(poz<=mid) change(ls,poz,k);
else change(rs,poz,k);
push_up(now);
}
int query(int now,int l,int r)
{
if(tr[now].l>=l&&tr[now].r<=r)
{
return tr[now].Max;
}
int mid=(tr[now].l+tr[now].r)>>1;
int res=0;
if(l<=mid) res=max(res,query(ls,l,r));
if(r>mid) res=max(res,query(rs,l,r));
return res;
}
}
bool vis[MAXN+5];
int pri[MAXN+5],phi[MAXN+5],sta[MAXN],poz[MAXN],p[MAXN],num,cnt,q,n;
void Init()
{
phi[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(!vis[i])
{
pri[++num]=i;
phi[i]=i-1;
}
for(int j=1;j<=num&&i*pri[j]<=MAXN;j++)
{
int now=i*pri[j];
vis[now]=true;
if(i%pri[j]==0)
{
phi[now]=phi[i]*pri[j];
break;
}
phi[now]=phi[i]*(pri[j]-1);
}
}
}
inline void Print(int x){
static char tmp[250];
if(x == 0) putchar('0');
int t = 0;
while(x) tmp[++t] = x % 10 + '0' , x /= 10;
while(t) putchar(tmp[t]) , t--;
putchar('\n');
}
signed main()
{
Init();
cin>>n;
for(int i=1;i<=n;i++)
{
p[i]=read();
poz[p[i]]=i;
}
// cout<<"line:\n";
// for(int i=1;i<=n;i++) cout<<phi[p[i]]<<" ";
for(int G=1;G<=n;G++)
{
vector<pair<int,int> >Mid;
for(int i=G;i<=n;i+=G)
{
Mid.push_back(make_pair(poz[i],phi[i]));
}
sort(Mid.begin(),Mid.end());
int top=0;
for(int i=0;i<Mid.size();i++)
{
while(top&&Mid[sta[top]].second<=Mid[i].second)
{
++cnt;
li[cnt].l=Mid[sta[top]].first;
li[cnt].r=Mid[i].first;
li[cnt].val=Mid[sta[top]].second*Mid[i].second*G/phi[G];
top--;
}
sta[++top]=i;
}
top=0;
for(int i=Mid.size()-1;i>=0;i--)
{
while(top&&Mid[sta[top]].second<=Mid[i].second)
{
++cnt;
// cout<<M
li[cnt].l=Mid[i].first;
li[cnt].r=Mid[sta[top]].first;
li[cnt].val=Mid[i].second*Mid[sta[top]].second*G/phi[G];
top--;
}
sta[++top]=i;
}
}
scanf("%lld",&q);
for(int i=1;i<=q;i++)
{
++cnt;
li[cnt].id=i;
li[cnt].l=read();
li[cnt].r=read();
}
Seg::build(1,1,n);
sort(li+1,li+1+cnt,[=](node a,node b){if(a.r==b.r) return a.id<b.id;return a.r<b.r;});
for(int i=1;i<=cnt;i++)
{
if(li[i].id)
{
Ans[li[i].id]=Seg::query(1,li[i].l,li[i].r);
}
else
{
Seg::change(1,li[i].l,li[i].val);
}
}
for(int i=1;i<=q;i++)
{
Print(Ans[i]);
}
}

不得不写成树状数组

/*
如果写线段树的话,建议写zkw
亲身验证普通线段树只能45pts
*/
#include<bits/stdc++.h>
#define int long long
#define MAXN 500005
using namespace std;
struct node
{
int l,r,id,val;
}li[MAXN<<5];
int Ans[MAXN];
template<class T>
T Read()
{
T x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int (*read)()=Read<int>;
#define read Read<int>
bool vis[MAXN+5];
int pri[MAXN+5],phi[MAXN+5],sta[MAXN],poz[MAXN],p[MAXN],num,cnt,q,n;
namespace Seg
{
int tr[MAXN];
int lowbit(int x)
{
return x&(-x);
}
void Insert(int x,int k)
{
while(x) tr[x]=max(tr[x],k),x-=lowbit(x);
}
int Query(int x)
{
int res=0;
while(x<=n) res=max(res,tr[x]),x+=lowbit(x);
return res;
}
// #define ls (now<<1)
// #define rs ((now<<1)|1)
// struct Tree
// {
// int l,r,Max;
// }tr[MAXN<<4];
// void build(int now,int l,int r)
// {
// tr[now].l=l;
// tr[now].r=r;
// if(l==r) return ;
// int mid=(l+r)>>1;
// build(ls,l,mid);
// build(rs,mid+1,r);
// }
// void push_up(int now)
// {
// tr[now].Max=max(tr[ls].Max,tr[rs].Max);
// }
// void change(int now,int poz,int k)
// {
// if(tr[now].l==poz&&tr[now].r==poz)
// {
// tr[now].Max=max(tr[now].Max,k);
// return;
// }
// int mid=(tr[now].l+tr[now].r)>>1;
// if(poz<=mid) change(ls,poz,k);
// else change(rs,poz,k);
// push_up(now);
// }
// int query(int now,int l,int r)
// {
// if(tr[now].l>=l&&tr[now].r<=r)
// {
// return tr[now].Max;
// }
// int mid=(tr[now].l+tr[now].r)>>1;
// int res=0;
// if(l<=mid) res=max(res,query(ls,l,r));
// if(r>mid) res=max(res,query(rs,l,r));
// return res;
// }
} void Init()
{
phi[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(!vis[i])
{
pri[++num]=i;
phi[i]=i-1;
}
for(int j=1;j<=num&&i*pri[j]<=MAXN;j++)
{
int now=i*pri[j];
vis[now]=true;
if(i%pri[j]==0)
{
phi[now]=phi[i]*pri[j];
break;
}
phi[now]=phi[i]*(pri[j]-1);
}
}
}
inline void Print(int x)
{
static char tmp[250];
if(x == 0) putchar('0');
int t = 0;
while(x) tmp[++t] = x % 10 + '0' , x /= 10;
while(t) putchar(tmp[t]) , t--;
putchar('\n');
}
signed main()
{
Init();
cin>>n;
for(int i=1;i<=n;i++)
{
p[i]=read();
poz[p[i]]=i;
}
// cout<<"line:\n";
// for(int i=1;i<=n;i++) cout<<phi[p[i]]<<" ";
for(int G=1;G<=n;G++)
{
vector<pair<int,int> >Mid;
for(int i=G;i<=n;i+=G)
{
Mid.push_back(make_pair(poz[i],phi[i]));
}
sort(Mid.begin(),Mid.end());
int top=0;
for(int i=0;i<Mid.size();i++)
{
while(top&&Mid[sta[top]].second<=Mid[i].second)
{
++cnt;
li[cnt].l=Mid[sta[top]].first;
li[cnt].r=Mid[i].first;
li[cnt].val=Mid[sta[top]].second*Mid[i].second*G/phi[G];
top--;
}
sta[++top]=i;
}
top=0;
for(int i=Mid.size()-1;i>=0;i--)
{
while(top&&Mid[sta[top]].second<=Mid[i].second)
{
++cnt;
// cout<<M
li[cnt].l=Mid[i].first;
li[cnt].r=Mid[sta[top]].first;
li[cnt].val=Mid[i].second*Mid[sta[top]].second*G/phi[G];
top--;
}
sta[++top]=i;
}
}
scanf("%lld",&q);
for(int i=1;i<=q;i++)
{
++cnt;
li[cnt].id=i;
li[cnt].l=read();
li[cnt].r=read();
}
sort(li+1,li+1+cnt,[=](node a,node b){if(a.r==b.r) return a.id<b.id;return a.r<b.r;});
for(int i=1;i<=cnt;i++)
{
if(li[i].id)
{
Ans[li[i].id]=Seg::Query(li[i].l);
}
else
{
Seg::Insert(li[i].l,li[i].val);
}
}
for(int i=1;i<=q;i++)
{
Print(Ans[i]);
}
}

6.8 NOI 模拟的相关教程结束。

《6.8 NOI 模拟.doc》

下载本文的Word格式文档,以方便收藏与打印。