P6177 Count on a tree II/【模板】树分块 题解
写了一点神奇的东西,具体来说是 @critnos 的代码实现。
这题确实有可以跑得飞快的平方除以
1. 寻找关键点
我们随机出
2. 具体做法
我们在找出
接下来,我们以
然后处理完了这些,我们可以开始查询。查询主要就是暴力计算散点,对于
具体来说,每次查询,我们对于还原后的
-
-
-
其他情况。此时我们一定有
fx=lc 与fy=lc 恰有一个满足,由于x,y 对称,我们不妨假设fx=lc 。那么我们可以从fy 开始,不断往上跳最近的关键点直到再跳一次关键点的深度就会小于等于x 的深度。假设这个关键点是ty ,那么答案就是x\to lca(x,ty) 的散点,ty\to lca(x,ty) 的散点和y\to fy 的散点,再加上fy\to ty 的整块。时间复杂度O(\sqrt n) 。
在跳的时候,为了保证每种颜色只被算一次,我们可以维护一个桶记录我们已经算过了哪些颜色。为了避免复杂度退化,我们清空桶的时候再重复以上的跳法就好了。
3. 代码
主要长度在于有很多重复内容并且很好理解的分类讨论。
#include<bits/stdc++.h>
using namespace std;
const int _=1e5+5,o=210;
int n,cnt,v,k,c,m,t,a[_],b[_],f[18][_],lg[_],p[_],q[_];
int dep[_],dfn[_],tot,nr[_],fa[_],h[_],s[o][_],pa[o][o];
vector<int>e[_];
int get(int x,int y){
return dep[x]<dep[y]?x:y;
}
void dfs(int x,int fat){
f[0][dfn[x]=++cnt]=x;
dep[x]=dep[fa[x]=fat]+1;
for(auto y:e[x])
if(y!=fat)dfs(y,x);
}
void pas(int x,int fat,int rt){
if(!h[a[x]]++)tot++;
if(p[x])pa[rt][p[x]]=tot;
for(auto y:e[x])
if(y!=fat)pas(y,x,rt);
if(!--h[a[x]])tot--;
}
void pre(int x,int nea){
nr[x]=nea;h[a[x]]++;
if(p[x])
for(int i=1;i<=v;i++)s[p[x]][i]=h[i];
for(auto y:e[x])
if(y!=fa[x])pre(y,p[x]?x:nea);
h[a[x]]--;
}
int lca(int x,int y){
if(x==y)return x;
if(dfn[x]>dfn[y])swap(x,y);
int ln=lg[dfn[y]-dfn[x]];
return fa[get(f[ln][dfn[x]+1],f[ln][dfn[y]-(1<<ln)+1])];
}
int main(){
srand(time(0));
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;c=k=sqrt(n)/2;
for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);v=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+v+1,a[i])-b;
for(int i=1,x,y;i<n;i++)cin>>x>>y,e[x].push_back(y),e[y].push_back(x);
dfs(1,0);
for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=16;i++)
for(int j=1;j<=n-(1<<i-1);j++)f[i][j]=get(f[i-1][j],f[i-1][j+(1<<i-1)]);
p[q[1]=1]=p[0]=1;
for(int i=2;i<=k;i++){
while(p[q[i]])q[i]=rand()%(n-1)+1;
p[q[i]]=1;
}
sort(q+1,q+k+1,[&](int x,int y){return dfn[x]<dfn[y];});
for(int i=1;i<k;i++)q[++c]=lca(q[i],q[i+1]);
sort(q+1,q+c+1);c=unique(q+1,q+c+1)-q-1;
for(int i=1;i<=c;i++)p[q[i]]=i;
for(int i=1;i<=c;i++)pas(q[i],0,i);
pre(1,0);
for(int x,y,lc,fx,fy,ty,ans,la=0;m;m--){
cin>>x>>y;x^=la;
fx=p[x]?x:nr[x];fy=p[y]?y:nr[y];
lc=lca(fx,fy);ans=0;
if(lc!=fx&&lc!=fy){
for(int i=x;i!=fx;i=fa[i])
if(!h[a[i]]){
ans+=s[p[fx]][a[i]]+s[p[fy]][a[i]]+(a[i]==a[lc])==2*s[p[lc]][a[i]];
h[a[i]]=1;
}
for(int i=y;i!=fy;i=fa[i])
if(!h[a[i]]){
ans+=s[p[fx]][a[i]]+s[p[fy]][a[i]]+(a[i]==a[lc])==2*s[p[lc]][a[i]];
h[a[i]]=1;
}
for(int i=x;i!=fx;i=fa[i])h[a[i]]=0;
for(int i=y;i!=fy;i=fa[i])h[a[i]]=0;
cout<<(la=ans+pa[p[fx]][p[fy]])<<'\n';
}else if(fx==fy){
lc=lca(x,y);
for(int i=x;i!=fa[lc];i=fa[i])
if(!h[a[i]])ans++,h[a[i]]=1;
for(int i=y;i!=lc;i=fa[i])
if(!h[a[i]])ans++,h[a[i]]=1;
for(int i=x;i!=fa[lc];i=fa[i])h[a[i]]=0;
for(int i=y;i!=lc;i=fa[i])h[a[i]]=0;
cout<<(la=ans)<<'\n';
}else{
if(fy==lc)swap(x,y),swap(fx,fy);ty=fy;
while(dep[nr[ty]]>dep[x])ty=nr[ty];
lc=lca(x,y);
for(int i=x;i!=fa[lc];i=fa[i])
if(!h[a[i]]){
ans+=s[p[fy]][a[i]]+(a[i]==a[ty])==s[p[ty]][a[i]];
h[a[i]]=1;
}
for(int i=ty;i!=lc;i=fa[i])
if(!h[a[i]]){
ans+=s[p[fy]][a[i]]+(a[i]==a[ty])==s[p[ty]][a[i]];
h[a[i]]=1;
}
for(int i=y;i!=fy;i=fa[i])
if(!h[a[i]]){
ans+=s[p[fy]][a[i]]+(a[i]==a[ty])==s[p[ty]][a[i]];
h[a[i]]=1;
}
for(int i=x;i!=fa[lc];i=fa[i])h[a[i]]=0;
for(int i=ty;i!=lc;i=fa[i])h[a[i]]=0;
for(int i=y;i!=fy;i=fa[i])h[a[i]]=0;
cout<<(la=ans+pa[p[fy]][p[ty]])<<'\n';
}
}
return 0;
}
空间复杂度