题解:BZOJ#4771 七彩树

1.Descrption

给定一棵有 \(n\) 个节点的树,树上的每个节点有一种颜色,现在给定 \(m\) 个询问,形如 \((x,d)\),表示询问在以 \(x\) 为根的子树中,深度在 \([dep_x,dep_x+d]\) 之间的节点里,有多少种不同的颜色,询问强制在线。

2.Solution

我们首先考虑简化问题,如果将每个询问中的限制去掉,改为查询 \(x\) 的子树中有多少种不同的颜色的话应该怎么做?

首先我们得出 dfs 序,将树上问题转换为序列问题。

然后先将整棵树中的所有节点的贡献都视为 \(1\),但是我们考虑一对相同颜色的点 \(u,v\),在 \(u,v\)\(LCA\) 及其祖先节点的询问中,\(u,v\) 的贡献将被计算为 \(2\),而实际贡献只为 \(1\),所以我们需要在 \(LCA\) 处减一。

由此,不妨假定颜色为 \(col\) 的节点共有 \(x\) 个,我们需要在原树上找到 \(x-1\) 个点(注意,这里找到的点颜色同样可以是 \(col\),并且可以重复),和这 \(x\) 个点构成一棵虚树,使得每一棵子树中的权值和都恰好为 \(1\),这显然可以将这 \(x\) 个点按照 dfs 序排序,然后在相邻两个点的 \(LCA\) 处减一,从而构造出来,可以使用线段树维护区间和,下面有深度限制时亦然。

但是这个时候有深度限制,我们不妨“一层一层”的考虑,首先考虑深度为 \(1\) 的点,然后考虑深度为 \(2\) 的点,一直将所有的点考虑完。

具体的,我们使用若干个 vector 储存对应深度的点有哪些,从小到大枚举深度,加入对应的点,这个时候需要使用 \(n\) 个 set 记录每一种颜色现在已经加入了哪些元素(这里需要按照 dfs 序排序)。

(注:下面这一段的'在......处'皆指在其对应的 dfs 序处进行操作。)

每一次加入一个节点 \(x\),在 set 中找到 \(x\) 的前驱 \(pre\) 和后继 \(suf\),显然在没有加入这个节点的时候,\(pre\)\(suf\) 相邻,根据我们在没有深度限制的时候的分析,在 \(pre\)\(suf\)\(LCA\) 处被减一了,这个时候应该加回去,然后在 \(pre\)\(x\)\(LCA\)\(suf\)\(x\)\(LCA\) 处减一,最后,在 \(x\) 处加一。

由于强制在线,我们可以使用可持久化线段树,对于一个询问 \((x,d)\),相当于在 \(dep_x+d\) 这个版本的线段树中查询。

注意在查询时判断 \(dep_x+d\) 的大小是否超过了这棵树的深度,也注意特判 \(x\) 没有前驱和后继的情况!

当然也得注意哪里使用的是点的编号,哪里使用的是点的 dfs 序。

3.Code

/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=1e5+5,M=7e6+5;
int n,m,mxdepth;
int col[N];
int rt[N];//每一个深度对应的根  
int dep[N];//树上节点深度 
int cnt_dfn;// dfs 序 
int L[N],R[N],dfn[N];
int fa[20][N];
vector<int>Idx[N];//深度为 i 的点的编号
set<int>Now[N];//Now[i] 当前已经加入的颜色为 i 的点的 dfs 序  
struct Chain_forword_star{
	struct Edge{
		int v,nxt;
	}e[N];
	int n,cnt_edge;
	int head[N];
	void init(int _n){
		n=_n,cnt_edge=0;
		for(int i=1;i<=n;i++)head[i]=0;
	}
	void AddEdge(int u,int v){
		e[++cnt_edge]={v,head[u]};
		head[u]=cnt_edge;
	}	
}G;
struct Segment_tree{
	int num;
	int c[M],ls[M],rs[M];
	#define mid (l+r>>1)
	int New(){
		num++;
		c[num]=ls[num]=rs[num]=0;
		return num;
	}
	int copy(int p){
		int q=New();
		c[q]=c[p],ls[q]=ls[p],rs[q]=rs[p];
		return q;
	}
	void pushup(int p){
		c[p]=c[ls[p]]+c[rs[p]];
	}
	int build(int l,int r){
		int p=New();
		if(l==r)return p;
		ls[p]=build(l,mid),rs[p]=build(mid+1,r);
		return p;
	}
	int change(int p,int l,int r,int x,int v){
		int q=copy(p);
		if(l==r){
			c[q]+=v;
			return q;
		}
		if(mid>=x)ls[q]=change(ls[p],l,mid,x,v);
		else rs[q]=change(rs[p],mid+1,r,x,v);
		pushup(q);
		return q;
	}
	int query(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R)return c[p];
		if(mid>=L&&mid<R)return query(ls[p],l,mid,L,R)+query(rs[p],mid+1,r,L,R);
		if(mid>=L)return query(ls[p],l,mid,L,R);
		return query(rs[p],mid+1,r,L,R);
	}
}Set;
void init(){
	mxdepth=1;
	Set.num=cnt_dfn=0;
	G.init(n);
	for(int i=1;i<=n;i++){
		Idx[i].clear();
		Now[i].clear();
	}
}
void dfs(int u){
	tomax(mxdepth,dep[u]);
	L[u]=++cnt_dfn;
	dfn[cnt_dfn]=u;
	for(int i=1;i<=17;i++)
		fa[i][u]=fa[i-1][fa[i-1][u]];
	for(int i=G.head[u],v;i;i=G.e[i].nxt){
		v=G.e[i].v;
		dep[v]=dep[u]+1;
		dfs(v);
	}
	R[u]=cnt_dfn;
}
int LCA(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	int d=dep[u]-dep[v];
	for(int i=0;i<=17;i++)
		if(d&1<<i)u=fa[i][u];
	if(u==v)return u;
	for(int i=17;i>=0;i--)
		if(fa[i][u]!=fa[i][v])
			u=fa[i][u],v=fa[i][v];
	return fa[0][u];
}
int find_pre(int x,int bel){
	auto it=Now[bel].lower_bound(x);
	if(it==Now[bel].begin())return -1;
	return *(--it);
}
int find_suf(int x,int bel){
	auto it=Now[bel].upper_bound(x);
	if(it==Now[bel].end())return -1;
	return *it;
}
signed main(){
	int t;
	read(t);
	while(t--){
		read(n),read(m);
		init();
		for(int i=1;i<=n;i++)read(col[i]);
		for(int i=2;i<=n;i++){
			read(fa[0][i]);
			G.AddEdge(fa[0][i],i);
		}
		dep[1]=1;
		dfs(1);
		//Idx 中存的是点的编号  
		//Now 中存的是点对应的 dfs 序 
		//LCA 接受的参数是点的编号 
		//Set 中点对应的下标是 dfs 序 
		//L[u] 表示编号 u 对应的 dfs 序
		//dfn[x] 表示 dfs 序 x 对应的点的编号 
		for(int i=1;i<=n;i++)
			Idx[dep[i]].push_back(i);
		rt[0]=Set.build(1,n);
		for(int i=1,pre,suf;i<=mxdepth;i++){
			rt[i]=rt[i-1];
			for(int x:Idx[i]){
				rt[i]=Set.change(rt[i],1,n,L[x],1);
				pre=find_pre(L[x],col[x]);
				suf=find_suf(L[x],col[x]);
				if(pre!=-1&&suf!=-1)
					rt[i]=Set.change(rt[i],1,n,L[LCA(dfn[pre],dfn[suf])],1);
				if(pre!=-1)
					rt[i]=Set.change(rt[i],1,n,L[LCA(dfn[pre],x)],-1);
				if(suf!=-1)
					rt[i]=Set.change(rt[i],1,n,L[LCA(x,dfn[suf])],-1);
				Now[col[x]].insert(L[x]);
			}
		}
		for(int i=1,last=0,x,d;i<=m;i++){
			x=read()^last,d=read()^last;
			d=min(mxdepth,dep[x]+d);
			last=Set.query(rt[d],1,n,L[x],R[x]);
			write(last),Nxt;
		}
	}
}
posted @ 2025-04-14 19:59  陈牧九  阅读(20)  评论(0)    收藏  举报