BZOJ4771: 七彩树
4771: 七彩树
Time Limit: 5 Sec Memory Limit: 256 MBSubmit: 2490 Solved: 715
[Submit][Status][Discuss]
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
题解{
这个强制在线题目的第一想法应该是想办法求出这棵树每一个节点下的子树中出现的颜色的最小深度,并以此为键值,建立一棵线段树,
那么就可以很方便地统计出某一点子树的深度区间内的颜色数目。
我们现在再来考虑怎么维护这棵线段树(不难看出应当采用动态开点的方法)。
首先我们先把这个节点放入当前线段树,接下来我们就只要把它的儿子的线段树都合并在它自己所属的这棵线段树里面就好了、
问题时合并时怎么更新出现颜色的最小深度。
此时我们再在每个节点下面维护出现颜色的最小深度的线段树。
在每次合并时,先合并父节点和子节点上面所说的第二棵线段树,
如果有两个相同颜色的对应深度有不同,更新父节点第二棵线段树的信息,并在父节点第一棵线段树中删除对应的节点,然后将父节点和子节点的第一棵线段树合并。
那么这个问题就被高效的解决了。。。。
(多组数据如果全用memset会莫名地RE和TLE,所以应该有点瑰丽鬼泣的优化。。。。)
}
#include<bits/stdc++.h> #define ll long long #define N 100005 #define mid ((l+r)>>1) #define inf 1e9 #define min(a,b) (a>b?b:a) #define max(a,b) (a>b?a:b) int dep[N],n,m,col[N],head[N],tot,lastans; struct ed{ int nxt,to; }e[N*2]; void link(int u,int v){ e[tot].nxt=head[u]; e[tot].to=v; head[u]=tot++; } namespace tree{ //第一棵主席树维护每个节点的叶节点的颜色总数,以深度为键值。cnt和首字母小写 //第二棵维护每个节点下每种颜色的最小深度,以颜色为键值。tr和首字母大写 int idn,IDN,rs[N*180],ls[N*180],tr[N*25],rt[N],Rt[N],Rs[N*25],Ls[N*25],cnt[N*180]; void clear(){ idn=0,IDN=0,tot=0,lastans=0; memset(rt,0,sizeof(rt)); memset(Rt,0,sizeof(Rt)); memset(head,-1,sizeof(head)); } void add(int &x,int l,int r,int d,int num){ rs[++idn]=rs[x]; ls[idn]=ls[x]; cnt[idn]=cnt[x]; cnt[idn]+=num; x=idn; if(l==r)return; if(mid<d)add(rs[x],mid+1,r,d,num); else add(ls[x],l,mid,d,num); } int merge(int x,int y){ if((!x)||(!y))return x+y; int p=++idn; rs[p]=merge(rs[x],rs[y]); ls[p]=merge(ls[x],ls[y]); cnt[p]=cnt[x]+cnt[y]; return p; } void Add(int &x,int l,int r,int d,int c){ if(!x){ x=++IDN; tr[x]=inf; Rs[x]=Ls[x]=0; } if(l==r){ tr[x]=min(tr[x],d); return; } if(mid<c)Add(Rs[x],mid+1,r,d,c); else Add(Ls[x],l,mid,d,c); } int Merge(int x,int y,int l,int r,int RT){ if((!x)||(!y))return x+y; if(l==r){ add(rt[RT],1,n,max(tr[x],tr[y]),-1); tr[x]=min(tr[x],tr[y]); return x; } Rs[x]=Merge(Rs[x],Rs[y],mid+1,r,RT); Ls[x]=Merge(Ls[x],Ls[y],l,mid,RT); return x; } int query(int x,int l,int r,int L,int R){ if((!x)||(!cnt[x]))return 0; if(l>=L&&r<=R)return cnt[x]; if(mid<L)return query(rs[x],mid+1,r,L,R); else if(mid>=R)return query(ls[x],l,mid,L,R); else return query(rs[x],mid+1,r,mid+1,R)+query(ls[x],l,mid,L,mid); } void build(){ for(int u=n;u>=1;u--){ add(rt[u],1,n,dep[u],1); Add(Rt[u],1,n,dep[u],col[u]); for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; rt[u]=merge(rt[u],rt[v]); Rt[u]=Merge(Rt[u],Rt[v],1,n,u); } } } } int main(){ freopen("1.in","r",stdin); freopen("qyp.out","w",stdout); int T;scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); tree::clear(); for(int i=1;i<=n;++i)scanf("%d",col+i); dep[1]=1; for(int i=2;i<=n;++i){ int j;scanf("%d",&j); dep[i]=dep[j]+1; link(j,i); } tree::build(); int x,d; for(int i=1;i<=m;++i){ scanf("%d%d",&x,&d); x=x^lastans; d=d^lastans; lastans=tree::query(tree::rt[x],1,n,dep[x],min(dep[x]+d,n)); printf("%d\n",lastans); } } return 0; }