七彩树(主席树+扫描线差分思想)(在线树上HH的项链)

题面

给定一棵 \(n\) 个点的有根树,编号依次为 \(1\)\(n\),其中 \(1\) 号点是根节点。每个节点都被染上了某一种颜色,其中第 \(i\) 个节点的颜色为 \(c_i\)

站在这棵色彩斑斓的树前面,你将面临 \(m\) 个问题。每个问题包含两个整数 \(x\)\(d\),表示询问 \(x\) 子树里且 \(depth\) 不超过 \(depth_x+d\)
​的所有点中出现了多少种本质不同的颜色。请写一个程序,快速回答这些询问。
强制在线,输入的\(x\)\(d\)\(xor\)上一次的答案

引入

看到这个题面,还让我强制在线,肯定是让我用主席树维护什么东西,因此,我一开始就想到了以深度为时间轴,节点\(dfn\)序作为树上信息,这样查的时候就可以直接查了,关键是如何用主席树维护这个“本质不同”的颜色数呢?

扫描线+差分

还记得"HH的项链吗",我们参考“HH的项链”的解法,对于一种颜色,及时过滤掉其重复出现的位置,但由于那题是一个序列,直接把前面出现过的相同颜色去了就可以,那放到本题的树上又应该怎么做呢?假设我们通过枚举深度到了节点\(x\),对于这个深度,怎样的出现过了的颜色算是重复了的呢?我们向上去找\(x\)的祖先\(y\),若以\(y\)为根的子树内已经出现了\(x\)的颜色,那么这个颜色必然是会重复计算的,并且是在计算\(y\)以及\(y\)的祖先时,所以我们在\(dfn_y\)\(-1\),在\(dfn_x\)\(+1\),就可以做到和HH的项链一样的效果了,现在的问题是如何找到这个祖先\(y\),我们考虑记录下来当前已经扫描到的颜色为\(col\)的点的\(dfn\)序,那么这个\(y\)子树中的和\(x\)有相同颜色的节点的\(dfn\)序和\(x\)一定是相邻的,所以对于每一种颜色,我们用一个set来查找\(dfn_x\)的前驱和后继,再用\(LCA\)\(y\)就好啦

恭喜你,学会了在线树上HH的项链!

code

#include<bits/stdc++.h>
#define lson tr[rt].ls
#define rson tr[rt].rs
#define olson tr[old].ls
#define orson tr[old].rs
#define pb push_back
using namespace std;
const int N=1e5+10;
int T,n,m,c[N],dfn[N],dep[N],siz[N],f[N][20],num,D,nw[N],rnk[N];
int root[N],tot;
vector<int>e[N],d[N];
set<int>s[N];
struct SegmentTree {int ls,rs,sum;}tr[N*50];
void clear() {
	num=tot=D=0;
	memset(tr,0,sizeof(tr));
	memset(nw,0,sizeof(nw));
	memset(root,0,sizeof(root));
	for(int i=1;i<=n;i++) s[i].clear(),e[i].clear(),d[i].clear();
}
void pushup(int rt) {tr[rt].sum=tr[lson].sum+tr[rson].sum;}
void update(int &rt,int old,int l,int r,int pos,int val) {
	tr[++tot]=tr[rt],rt=tot,lson=olson,rson=orson;
	if(l==r) return tr[rt].sum+=val,void();
	int mid=(l+r)>>1;
	if(pos<=mid) update(lson,olson,l,mid,pos,val);
	else update(rson,orson,mid+1,r,pos,val);
	pushup(rt);
}
int query(int rt,int l,int r,int L,int R) {
	if(!rt) return 0;
	if(L<=l&&r<=R) return tr[rt].sum;
	int mid=(l+r)>>1,ans=0;
	if(L<=mid) ans+=query(lson,l,mid,L,R);
	if(R>mid) ans+=query(rson,mid+1,r,L,R);
	return ans;
}
void dfs(int x,int fa) {
	dfn[x]=++num,rnk[num]=x;
	siz[x]=1;
	dep[x]=dep[fa]+1,D=max(D,dep[x]);
	d[dep[x]].pb(x);
	f[x][0]=fa;
	for(int i=1;i<=17;i++) f[x][i]=f[f[x][i-1]][i-1];
	for(int y:e[x]) {
		if(y==fa) continue;
		dfs(y,x);
		siz[x]+=siz[y];
	}
}
int LCA(int x,int y) {
	if(dep[x]>dep[y]) swap(x,y);
	for(int i=17;i>=0;i--) if(dep[f[y][i]]>=dep[x]) y=f[y][i];
	if(x==y) return x;
	for(int i=17;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
void Delete(int x,int col,int deep) {
	if(!nw[col]) return;
	else if(nw[col]==1) {
		auto it=s[col].lower_bound(0);
		int lca=LCA(x,rnk[*it]);
		update(root[deep],root[deep],1,n,dfn[lca],-1);
	}
	else {
		auto it=s[col].lower_bound(dfn[x]);
		if(it==s[col].end()) {
			it--;
			int lca=LCA(x,rnk[*it]);
			update(root[deep],root[deep],1,n,dfn[lca],-1);
		}
		else if(it==s[col].begin()) {
			int lca=LCA(x,rnk[*it]);
			update(root[deep],root[deep],1,n,dfn[lca],-1);
		}
		else {
			int lca1=LCA(x,rnk[*it]),lca2=LCA(x,rnk[*--it]);
			int lca=(dep[lca1]>dep[lca2]?lca1:lca2);
			update(root[deep],root[deep],1,n,dfn[lca],-1);
		}
	}
}
signed main() {
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--) {
		cin>>n>>m;
		clear();
		for(int i=1;i<=n;i++) cin>>c[i];
		for(int i=1,x;i<n;i++) cin>>x, e[i+1].pb(x),e[x].pb(i+1);
		dfs(1,0);
		for(int i=1;i<=D;i++) 
			for(int j=0;j<d[i].size();j++) {
				if(!j) update(root[i],root[i-1],1,n,dfn[d[i][j]],1);
				else update(root[i],root[i],1,n,dfn[d[i][j]],1);
				Delete(d[i][j],c[d[i][j]],i);
				nw[c[d[i][j]]]++;
				s[c[d[i][j]]].insert(dfn[d[i][j]]);
			}
		int ans=0;
		for(int i=1,x,dd;i<=m;i++) {
			cin>>x>>dd;
			x^=ans,dd^=ans;
			dd+=dep[x];
			ans=query(root[dd],1,n,dfn[x],dfn[x]+siz[x]-1);
			cout<<ans<<"\n";
		}
	}
	return 0;
}
posted @ 2026-08-16 21:11  技技技技技  阅读(0)  评论(0)    收藏  举报