七彩树(主席树+扫描线差分思想)(在线树上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;
}

浙公网安备 33010602011771号