[BZOJ]2589: Spoj 10707 Count on a tree II
Time Limit: 20 Sec Memory Limit: 400 MB
Description
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v),你需要回答u xor lastans和v这两个节点间有多少种不同的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
Input
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v),表示一组询问。
数据范围是N<=40000 M<=100000 点权在int范围内
Output
M行,表示每个询问的答案。
Sample Input
8 2
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5
3 8
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5
3 8
Sample Output
4
4
4
Solution
在线莫队(TLE):把树分成k块,每块选出一个点作为代表点,预处理出所有块的代表点到其他代表点路径上颜色种数和各种颜色的数量,每次询问找到两个询问点所在块,根据预处理出的信息可以在$O(\frac{n}{k})$时间内算出答案,复杂度$O(nk^{2}+\frac{qn}{k})$,适当调整k,总复杂度约为$O(n^{\frac{5}{3}})$,理论上很科学可是这题卡常……
正解树分块+可持久化块状数组:把树分成k块,每块选出一个点作为代表点,预处理出各块代表点到其他所有点路径上的颜色种数,对于每个询问x,y,我们令所在块的代表点深度较大的为x,代表点为u,则我们利用预处理的信息可以知道u到y的路径上的答案,接下来我们把x到u的路径并入答案中,只要能支持$O(1)$询问一种颜色是否在u到y的路径上出现过即可$O(\frac{n}{k})$完成,我们预处理出每个点到根路径上各种颜色出现的最大深度,那么如果u到根和y到根出现一种颜色的最大深度大等于lca(u,y)的深度,那么这种颜色就在u到y的路径上出现过,暴力计算是$O(n^{2})$的,用可持久化块状数组我们就能实现$O(\sqrt{n})$从一个点的父亲那里复制数组,$O(\sqrt{n})$时间内修改一个元素,$O(1)$查询一个值,总复杂度约为$O(n\sqrt{n})$。
Code
Code
在线莫队(TLE)
#include<cstdio> #include<algorithm> #include<map> using namespace std; inline int read() { int x,f=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')f=0; for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return f?x:-x; } #define MN 40000 #define K 713 #define LG 15 #define KS (MN/K) map<int,int> mp; struct edge{int nx,t;}e[MN*2+5]; int h[MN+5],en,c[MN+5],cnt,fa[LG+1][MN+5],d[MN+5],s[MN+5],ht[MN+5],q[MN+5],qr,b[MN+5],p[KS+2]; unsigned short ans[KS+2][KS+2],f[KS+2][KS+2][MN+5]; bool u[KS+2][KS+2][MN+5]; inline void ins(int x,int y) { e[++en]=(edge){h[x],y};h[x]=en; e[++en]=(edge){h[y],x};h[y]=en; } void pre(int x) { for(int i=h[x];i;i=e[i].nx)if(e[i].t!=fa[0][x]) { fa[0][e[i].t]=x;d[e[i].t]=d[x]+1; pre(e[i].t); s[x]+=s[e[i].t];ht[x]=max(ht[x],ht[e[i].t]+1); } if(++s[q[++qr]=x],(ht[x]=max(ht[x],1))==K||x<2) {for(p[++cnt]=x;s[x]--;)b[q[qr--]]=cnt;ht[x]=s[x]=0;} } inline void cal(int a,int b,int x) { (u[a][b][x]^=1)?f[a][b][c[x]]++?0:++ans[a][b]: --f[a][b][c[x]]?0:--ans[a][b]; } int lca(int x,int y) { int dx=d[x]-d[y],i; if(dx<0)swap(x,y),dx=-dx; for(i=0;dx;++i,dx>>=1)if(dx&1)x=fa[i][x]; if(x==y)return x; for(i=LG;i>=0;--i)if(fa[i][x]!=fa[i][y])x=fa[i][x],y=fa[i][y]; return fa[0][x]; } int main() { int n,m,i,j,x,y,l=0,lx=0,ly=0; n=read();m=read(); for(i=1;i<=n;++i)mp[c[i]=read()]?0:mp[c[i]]=++cnt; for(i=1;i<=n;++i)c[i]=mp[c[i]]; for(i=1;i<n;++i)ins(read(),read()); cnt=0;pre(1); for(i=1;i<=LG;++i)for(j=1;j<=n;++j)fa[i][j]=fa[i-1][fa[i-1][j]]; for(i=1;i<=cnt;++i)for(j=1;j<=cnt;++j) for(x=p[i],y=p[j];x!=y;) if(d[x]>d[y])cal(i,j,x),x=fa[0][x]; else cal(i,j,y),y=fa[0][y]; while(m--) { i=b[x=read()^l];j=b[y=read()]; if(x==lx&&y==ly){printf("%d\n",l);continue;} cal(i,j,q[qr=1]=lca(lx=x,ly=y)); while(x!=p[i])cal(i,j,q[++qr]=x),x=fa[0][x]; while(y!=p[j])cal(i,j,q[++qr]=y),y=fa[0][y]; printf("%d\n",l=ans[i][j]); for(x=1;x<=qr;++x)cal(i,j,q[x]); } }
正解
#include<cstdio> #include<algorithm> #include<map> using namespace std; inline int read() { int x,f=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')f=0; for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return f?x:-x; } #define MN 40000 #define K 200 #define LG 15 map<int,int> mp; struct edge{int nx,t;}e[MN*2+5]; int h[MN+5],en,c[MN+5],cnt,fa[LG+1][MN+5],d[MN+5],s[MN+5],ht[MN+5],q[MN+5],qn; int b[MN+5],p[K+1],u[MN+5],ans[K+1][MN+5],a[MN+5][K],v[MN+5][K],vn; inline void ins(int x,int y) { e[++en]=(edge){h[x],y};h[x]=en; e[++en]=(edge){h[y],x};h[y]=en; } void pre(int x) { for(int i=h[x];i;i=e[i].nx)if(e[i].t!=fa[0][x]) { fa[0][e[i].t]=x;d[e[i].t]=d[x]+1; pre(e[i].t); s[x]+=s[e[i].t];ht[x]=max(ht[x],ht[e[i].t]+1); } if(++s[q[++qn]=x],(ht[x]=max(ht[x],1))==K||x<2) {for(p[++cnt]=x;s[x]--;)b[q[qn--]]=cnt;ht[x]=s[x]=0;} } void dfs(int k,int x,int f) { if(!u[c[x]]++)++ans[k][x]; if(!f)ans[k][fa[0][x]]=ans[k][x],dfs(k,fa[0][x],x); else for(int i=h[x];i;i=e[i].nx)if(e[i].t!=f) ans[k][e[i].t]=ans[k][x],dfs(k,e[i].t,x); --u[c[x]]; } void build(int x) { int i,j=c[x]/K,k=c[x]%K; for(i=0;i<K;++i)a[x][i]=a[fa[0][x]][i]; for(++vn,i=0;i<K;++i)v[vn][i]=v[a[x][j]][i]; v[a[x][j]=vn][k]=d[x]; for(i=h[x];i;i=e[i].nx)if(e[i].t!=fa[0][x])build(e[i].t); } int lca(int x,int y) { int dx=d[x]-d[y],i; if(dx<0)swap(x,y),dx=-dx; for(i=0;dx;++i,dx>>=1)if(dx&1)x=fa[i][x]; if(x==y)return x; for(i=LG;i>=0;--i)if(fa[i][x]!=fa[i][y])x=fa[i][x],y=fa[i][y]; return fa[0][x]; } int vio(int x,int y) { int res=0; for(qn=0;x!=y;) d[x]>d[y]?(u[q[++qn]=c[x]]++?0:++res,x=fa[0][x]): (u[q[++qn]=c[y]]++?0:++res,y=fa[0][y]); u[q[++qn]=c[x]]++?0:++res; while(qn)u[q[qn--]]=0; return res; } int main() { int n,m,i,j,x,y,l=0; n=read();m=read(); for(i=1;i<=n;++i)mp[c[i]=read()]?0:mp[c[i]]=++cnt; for(i=1;i<=n;++i)c[i]=mp[c[i]]-1; for(i=1;i<n;++i)ins(read(),read()); cnt=0;pre(d[1]=1); for(i=1;i<=LG;++i)for(j=1;j<=n;++j)fa[i][j]=fa[i-1][fa[i-1][j]]; for(i=1;i<=cnt;++i)dfs(i,p[i],0); build(1); while(m--) { x=read()^l;y=read(); if(b[x]==b[y])l=vio(x,y); else { if(d[p[b[x]]]<d[p[b[y]]])swap(x,y); l=ans[b[x]][y];j=d[lca(x,y)]; for(i=x;i!=p[b[x]];i=fa[0][i])if(!u[c[i]]++) if(max(v[a[p[b[x]]][c[i]/K]][c[i]%K],v[a[y][c[i]/K]][c[i]%K])<j)++l; for(i=x;i!=p[b[x]];i=fa[0][i])u[c[i]]=0; } printf("%d\n",l); } }