P6177-Count on a tree II/[模板]树分块

正题

题目链接:https://www.luogu.com.cn/problem/P6177


题目大意

\(n\)个点的一棵树\(m\)次询问树上颜色。
强制在线

\(1\leq n\leq 4\times 10^5,1\leq m\leq 10^5,0\leq val_i<2^{31}\)


解题思路

把所有深度为\(\sqrt n\)并且下面至少有\(\sqrt n\)的深度的点标记,这样保证关键点数量不超过\(\sqrt n\)

然后每个点到他周围关键点的距离也不会超过 \(\sqrt n\)

这样可以处理出关键点两两之间的颜色\(bitset\),然后每次路径找两个最近的关键点爆做就好了。

时间复杂度\(O((m+n)(\sqrt n+\frac{n}{\omega}))\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<cmath>
using namespace std;
const int N=41000,T=300,M=(4e4)/T+10;
struct node{
	int to,next;
}a[N<<1];
int n,m,tot,cnt,sum,fa[N],v[N],top[N],ls[N],d[N],dep[N],w[N],b[N],mark[N],dfn[N],ed[N],g[M][M];
bitset<N> f[M][M],bt;
void addl(int x,int y){
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;return;
}
void dfs(int x){
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa[x])continue;
		dep[y]=dep[x]+1;
		fa[y]=x;dfs(y);
		d[x]=max(d[x],d[y]+1);
	}
	if(dep[x]%T==0&&d[x]>=T)
		mark[x]=++cnt;
	return;
}
void dFs(int x){
	if(mark[x])top[x]=x;dfn[x]=++cnt;
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa[x])continue;
		top[y]=top[x];dFs(y);
	}
	ed[x]=cnt;
	return;
}
void calc(int x,int p,int fa){
	if(!v[w[x]])bt[w[x]]=1,sum++;v[w[x]]++;
	if(mark[x])f[p][mark[x]]=bt,g[p][mark[x]]=sum;
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(y==fa)continue;
		calc(y,p,x);
	}
	v[w[x]]--;if(!v[w[x]])bt[w[x]]=0,sum--;
	return;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]),b[i]=w[i];
	sort(b+1,b+1+n);
	int L=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++)
		w[i]=lower_bound(b+1,b+1+L,w[i])-b;
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		addl(x,y);addl(y,x);
	}
	dfs(1);cnt=0;dFs(1);
	for(int i=1;i<=n;i++)
		if(mark[i])calc(i,mark[i],i);
	int last=0;
	while(m--){
		int x,y;
		scanf("%d%d",&x,&y);
		x^=last;
		if(top[x]==top[y]){
			int xx=x,yy=y,ans=0;
			while(x!=y){
				if(dep[x]<dep[y])swap(x,y);
				if(!v[w[x]])ans++,v[w[x]]=1;
				x=fa[x];
			}
			if(!v[w[x]])ans++,v[w[x]]=1;
			printf("%d\n",ans);last=ans;
			x=xx;y=yy;
			while(x!=y){
				if(dep[x]<dep[y])swap(x,y);
				v[w[x]]=0;x=fa[x];
			}
			v[w[x]]=0;
		}
		else{
			if(dfn[x]>=dfn[y]&&dfn[x]<=ed[y])swap(x,y);
			if(dfn[y]>=dfn[x]&&dfn[y]<=ed[x]){
				int z=top[y];
				while(top[fa[z]]!=top[x])
					z=top[fa[z]];
				bt=f[mark[z]][mark[top[y]]];
				int ans=g[mark[z]][mark[top[y]]];
				while(y!=top[y]){
					if(!bt[w[y]])ans++,bt[w[y]]=1;
					y=fa[y];
				}
				while(z!=x){
					if(!bt[w[z]])ans++,bt[w[z]]=1;
					z=fa[z];
				}
				if(!bt[w[z]])ans++,bt[w[z]]=1;
				printf("%d\n",ans);last=ans;
			}
			else{
				bt=f[mark[top[x]]][mark[top[y]]];
				int ans=g[mark[top[x]]][mark[top[y]]];
				while(x!=top[x]){
					if(!bt[w[x]])ans++,bt[w[x]]=1;
					x=fa[x];
				}
				while(y!=top[y]){
					if(!bt[w[y]])ans++,bt[w[y]]=1;
					y=fa[y];
				}
				printf("%d\n",ans);last=ans;
			}
		}
	}
	return 0;
}
posted @ 2021-08-19 08:50  QuantAsk  阅读(70)  评论(0编辑  收藏  举报