大概看了一眼网上的题解,跟块爷一样都写的会被卡的分块。(反正块爷出的题也不会卡自己。。)
这里说一种比较科学的做法。就是用块链维护dfs序。
维护每个节点按dfs序是在哪个块的哪个位置,对每个块维护块中节点的最浅深度、它的下一个块是哪个块,块中节点按dfs序排序的序列,按权值排序的序列。
一开始的时候每B个分一块,最后一块节点数 ≤B 。查询的时候在两边的块暴力,在中间的块里二分,时间复杂度 O(B+(n+m)BlogB) ;修改的时候暴力改,时间复杂度 O(B) ;插入的时候也是暴力改,如果块的大小等于2B了,就把它拆成两个,时间复杂度 O(B) 。预处理的时间复杂度是 O(nlog2n)
这样取B= (n+m)log2n−−−−−−−−−−−√ ,时间复杂度 O(nlog2n+(n+m)(n+m)log2n−−−−−−−−−−−√)
第一次写块链,搞了很久。。记了每个块有哪些点,每个点在哪个块的哪个位置,然后要记两种排序。。
这种写法比较好写(只有5K+。。)
但是作为一名理论计算机科学家,我们需要追求更优越的时间复杂度。
显然平衡树套线段树可以做到 O(nlog22n) ,但是毫无疑问常数巨大,所以我们继续思考块链的做法。
参考带插入区间第K大的 O(nn√) 做法,我们发现可以用一个块链维护权值排序后的序列,一个块链维护dfs序排序后的序列,然后在后者中维护每个权值出现次数的前缀和和每块权值出现次数的前缀和。这样就可以做到 O((n+m)n+m−−−−−√) 了!(虽然这样随便一写就7K+。。)
代码:
代码( O((n+m)(n+m)log2n−−−−−−−−−−−√) ):
#include<cstdio>
#include<iostream>
using namespace std;
#include<cassert>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
const int N=3e4+5,M=3e4+5;
int n;
char * cp=(char *)malloc(2000000);
void in(int &x){
bool flag=0;
while(*cp<'0'||*cp>'9')flag=*cp++=='-';
for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
if(flag)x=-x;
}
int ptr[N],next[N<<1],succ[N<<1],etot=1;
void addedge(int from,int to){
next[etot]=ptr[from],ptr[from]=etot,succ[etot++]=to;
}
const int B=650;
//块的个数是2N/B,块的大小是2B。
struct PS{
int bpos,pos;
}pos[N<<1];
int block[2000+5][2000+5];
struct SS{
int num,w;
bool operator < (const SS &o)const{
return w<o.w;
}
}sorted[2000+5][2000+5];
int w[N<<1],ptot;
int bnext[2000+5],btot=1;
int cur[N],fa[N];
int depth[N<<1],mindep[2000+5];
void out(){
for(int i=1;i<btot;++i){
printf("------%d-----\n",i);
printf("sorted by dfn:");
for(int j=1;j<=block[i][0];++j)printf("%d ",block[i][j]);
puts("");
printf("sorted by w:");
for(int j=1;j<=block[i][0];++j)printf("{%d,%d} ",sorted[i][j].num,sorted[i][j].w);
puts("");
printf("mindep=%d\n",mindep[i]);
printf("bnext=%d\n",bnext[i]);
}
printf("pos:\n");
for(int i=1;i<ptot;++i)printf("pos[%d]={%d,%d}\n",i,pos[i].bpos,pos[i].pos);
}
void build(){
for(int i=n;i;--i)cur[i]=ptr[i];
memset(mindep,127,sizeof(mindep));
for(int node=1;node;){
if(cur[node]==ptr[node]){
depth[node]=depth[fa[node]]+1;
if(block[btot][0]==B)++btot;
block[btot][++block[btot][0]]=node;
sorted[btot][block[btot][0]]=(SS){node,w[node]};
pos[node]=(PS){btot,block[btot][0]};
mindep[btot]=min(mindep[btot],depth[node]);
}
if(cur[node])
if(succ[cur[node]]==fa[node])cur[node]=next[cur[node]];
else{
fa[succ[cur[node]]]=node;
node=succ[cur[node]];
cur[fa[node]]=next[cur[fa[node]]];
}
else node=fa[node];
}
++btot;
for(int i=btot-2;i;--i)bnext[i]=i+1;
for(int i=1;i<btot;++i)sort(sorted[i]+1,sorted[i]+block[i][0]+1);
//out();
}
void update(int u){
int i=pos[u].bpos,j=block[i][0];
while(sorted[i][j].num!=u)--j;
for(;j<block[i][0];++j)sorted[i][j]=sorted[i][j+1];
int x=lower_bound(sorted[i]+1,sorted[i]+block[i][0],(SS){0,w[u]})-sorted[i];
for(j=block[i][0];j>x;--j)sorted[i][j]=sorted[i][j-1];
sorted[i][x]=(SS){u,w[u]};
}
int main(){
freopen("bzoj_3720.in","r",stdin);
freopen("bzoj_3720.out","w",stdout);
fread(cp,1,2000000,stdin);
in(n);
int u,v;
for(int i=n;--i;){
in(u),in(v);
addedge(u,v),addedge(v,u);
}
for(int i=1;i<=n;++i)in(w[i]);
ptot=n+1;
build();
int m;
in(m);
int op,x;
int lastans=0;
int i,j,tmp;
while(m--){
in(op),in(u),in(x);
u^=lastans,x^=lastans;
//printf("----%d,%d,%d------\n",op,u,x);
switch(op){
case 0:
lastans=0;
lastans+=w[u]>x;
for(i=pos[u].bpos,j=pos[u].pos+1;j<=block[i][0]&&depth[block[i][j]]>depth[u];++j){
lastans+=w[block[i][j]]>x;
//printf("%d w[%d] %d\n",block[i][j],w[block[i][j]],x);
}
if(j>block[i][0]){
for(i=bnext[pos[u].bpos];i&&mindep[i]>depth[u];i=bnext[i]){
lastans+=block[i][0]-(upper_bound(sorted[i]+1,sorted[i]+block[i][0]+1,(SS){0,x})-sorted[i])+1;
//printf("%d\n",(upper_bound(sorted[i]+1,sorted[i]+block[i][0]+1,(SS){0,x})-sorted[i]));
}
for(j=1;i&&depth[block[i][j]]>depth[u];++j)lastans+=w[block[i][j]]>x;
}
printf("%d\n",lastans);
break;
case 1:
w[u]=x;
update(u);
break;
case 2:
depth[ptot]=depth[u]+1;
w[ptot]=x;
x=pos[u].pos;
for(i=pos[u].bpos,j=++block[i][0]-1;j>x;--j){
block[i][j+1]=block[i][j];
++pos[block[i][j]].pos;
}
block[i][j+1]=ptot;
pos[ptot]=(PS){pos[u].bpos,x+1};
sorted[i][block[i][0]]=(SS){ptot,w[ptot]};
update(ptot);
if(block[i][0]==B<<1){
bnext[btot]=bnext[i];
bnext[i]=btot;
i=pos[u].bpos;
block[i][0]=0;
memcpy(block[btot]+1,block[i]+B+1,sizeof(int)*B);
for(j=B;j;--j)pos[block[btot][j]]=(PS){btot,j};
for(j=1;j<=B<<1;++j){
tmp=pos[sorted[i][j].num].bpos;
//cout<<sorted[i][j].num<<"->"<<tmp<<endl;
sorted[tmp][++block[tmp][0]]=sorted[i][j];
}
mindep[i]=depth[block[i][1]];
for(j=B;j>1;--j)mindep[i]=min(mindep[i],depth[block[i][j]]);
mindep[btot]=depth[block[btot][1]];
for(j=B;j>1;--j)mindep[btot]=min(mindep[btot],depth[block[btot][j]]);
++btot;
}
++ptot;
break;
}
//out();
}
}
总结:
①分块写之前一定要想好实现的细节!