Description
给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点。每个节点都被染上了某一种颜色,其中第i个节
点的颜色为c[i]。如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色。定义depth[i]为i节点与根节点的距离
,为了方便起见,你可以认为树上相邻的两个点之间的距离为1。站在这棵色彩斑斓的树前面,你将面临m个问题。
每个问题包含两个整数x和d,表示询问x子树里且depth不超过depth[x]+d的所有点中出现了多少种本质不同的颜色
。请写一个程序,快速回答这些询问。
Input
第一行包含一个正整数T(1<=T<=500),表示测试数据的组数。
每组数据中,第一行包含两个正整数n(1<=n<=100000)和m(1<=m<=100000),表示节点数和询问数。
第二行包含n个正整数,其中第i个数为c[i](1<=c[i]<=n),分别表示每个节点的颜色。
第三行包含n-1个正整数,其中第i个数为f[i+1](1<=f[i]<i),表示节点i+1的父亲节点的编号。
接下来m行,每行两个整数x(1<=x<=n)和d(0<=d<n),依次表示每个询问。
输入数据经过了加密,对于每个询问,如果你读入了x和d,那么真实的x和d分别是x xor last和d xor last,
其中last表示这组数据中上一次询问的答案,如果这是当前数据的第一组询问,那么last=0。
输入数据保证n和m的总和不超过500000。
Output
对于每个询问输出一行一个整数,即答案。
Sample Input
1
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1
Sample Output
1
2
3
1
1
2
1
1
2
3
1
1
2
1
1
HINT
Source
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~dfs序+主席树+set+思路~
我们首先假设每个点的贡献是1,那么同色两点对于其lca的贡献就要-1,所以我们记录将同一个深度的点按照dfs序排序并记录在set中,那么dfs序中相邻两点就是应该减去的贡献。注意如果两边的点与中间点的贡献值都减去了,那么一定还要加上两边点的贡献值,这里用到了容斥原理。
这些贡献值的信息储存在主席树中。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int t,n,m,x,y,a[100001],id[100001],fi[100001],ne[200001],w[200001],cnt,dep[100001];
int fa[100001][18],siz[100001],dfn[100001],rt[100001],v[10000001],c[10000001][2],ans;
struct node{
int id,x;
};
set<node> se[100001];
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
bool operator < (node u,node v)
{
return u.id<v.id;
}
bool cmp(int u,int v)
{
return dep[u]<dep[v];
}
void add(int u,int v)
{
w[++cnt]=v;ne[cnt]=fi[u];fi[u]=cnt;
}
void dfs(int u)
{
siz[u]=1;dfn[u]=++cnt;
for(int i=1;i<=17;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=fi[u];i;i=ne[i]) dep[w[i]]=dep[u]+1,fa[w[i]][0]=u,dfs(w[i]),siz[u]+=siz[w[i]];
}
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=17;~i;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=17;~i;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void add(int &now,int las,int l,int r,int u,int vv)
{
now=++cnt;
memcpy(c[now],c[las],sizeof(c[las]));
v[now]=v[las]+vv;
if(l==r) return;
int mid=l+r>>1;
if(mid>=u) add(c[now][0],c[las][0],l,mid,u,vv);
else add(c[now][1],c[las][1],mid+1,r,u,vv);
}
int cal(int now,int l,int r,int u,int vv)
{
if(!now) return 0;
if(l>=u && r<=vv) return v[now];
int mid=l+r>>1,no=0;
if(u<=mid) no+=cal(c[now][0],l,mid,u,vv);
if(vv>mid) no+=cal(c[now][1],mid+1,r,u,vv);
return no;
}
int main()
{
t=read();
while(t--)
{
n=read();m=read();dep[1]=1;cnt=0;ans=0;
memset(fi,0,sizeof(fi));
memset(fa,0,sizeof(fa));
for(int i=1;i<=n;i++) a[i]=read(),id[i]=i;
for(int i=2;i<=n;i++) x=read(),add(x,i);
cnt=0;dfs(1);
sort(id+1,id+n+1,cmp);cnt=0;
for(int i=1,now=1;i<=n;i++)
{
rt[i]=rt[i-1];
for(;now<=n && dep[id[now]]==i;now++)
{
x=id[now];
add(rt[i],rt[i],1,n,dfn[x],1);
set<node> :: iterator k=se[a[x]].insert((node){dfn[x],x}).first,k1=k,k2=k;k1--;k2++;
if(k!=se[a[x]].begin() && k2!=se[a[x]].end()) add(rt[i],rt[i],1,n,dfn[lca((*k1).x,(*k2).x)],1);
if(k!=se[a[x]].begin()) add(rt[i],rt[i],1,n,dfn[lca((*k1).x,x)],-1);
if(k2!=se[a[x]].end()) add(rt[i],rt[i],1,n,dfn[lca(x,(*k2).x)],-1);
}
}
while(m--)
{
x=read()^ans;y=read()^ans;
printf("%d\n",ans=cal(rt[min(dep[x]+y,n)],1,n,dfn[x],dfn[x]+siz[x]-1));
}
for(int i=1;i<=n;i++) se[i].clear();
}
return 0;
}

本文介绍了一种解决树形结构问题的方法,结合DFS序、主席树和Set数据结构,通过示例代码详细阐述如何高效地处理涉及节点颜色查询的问题。
335

被折叠的 条评论
为什么被折叠?



