5709 - Claris Loves Painting

给定一棵树,树上每个节点有一个颜色,每次询问以x节点为根的子树,深度小于等于d的部分共有几种颜色。
以下摘自胡主力的ppt:
先考虑一个弱化版,询问是询问一个结点的整棵子树的颜色数
先处理出树的dfs 序,考虑用 set 维护每种颜色结点的 dfs 序,从根开始一层一层加入结点
每加入一个点i ,就在 set 中找到 dfs 序在它之前和之后的和它颜色相同的两个点,设这两个点为 x y lca ( i,x ) lca ( i,y ) 中深度较大的点为 t ,可以发现从 i t (不包括 t )的这条链上所有点,子树的颜色数都多了 1
至于为什么我也不知道。反正这题就变成了每次给一条链+1,查询某个节点的值。对此我们可以进行差分,就变成了修改一个点的值,查询子树的和。然后用线段树处理dfs序就可以搞出来了。
然后回到原问题。发现可以用主席树搞出只加到深度为deep[x]+d的线段树,进行查询就好了。
这也太难写了吧OMG!!
下面是我5小时的心血。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define G getchar()
#define LL long long
#define pll pair<int,int>
#define mkp make_pair
#define X first
#define Y second
const int N=100005,NN=8000000;
int n,m,w[N];
int tot,he[N],ne[N<<1],to[N<<1],ans;
int dad[N],son[N],pre[N],dep[N],sz[N],dfn[N],cnt,nfd[N];
int bfn[N],key[N],md;
int sum[NN],lc[NN],rc[NN],id,rt[N<<1],ln;
set<int>st[N];
void read(int &x){
	char ch=G;
	while(ch<48||ch>57)ch=G;
	for(x=0;ch>47&&ch<58;ch=G)x=x*10+ch-48;
}
void add(int x,int y){
	to[++tot]=y;ne[tot]=he[x];he[x]=tot;
}
void DFS1(int x){
	int i,y;sz[x]=1;
	for(i=he[x];i;i=ne[i]){
		pre[y=to[i]]=x;dep[y]=dep[x]+1;
		DFS1(y);
		if(sz[son[x]]<sz[y])son[x]=y;
		sz[x]+=sz[y];
	}
}
void DFS2(int x){
	int i,y;nfd[dfn[x]=++cnt]=x;
	dad[x]=son[pre[x]]==x?dad[pre[x]]:x;
	if(son[x])DFS2(son[x]);
	for(i=he[x];i;i=ne[i])if(!dad[y=to[i]])
		DFS2(y);
}
int lca(int x,int y){
	int fx=dad[x],fy=dad[y];
	while(fx!=fy)
		if(dep[fx]>dep[fy])fx=dad[x=pre[fx]];
		else fy=dad[y=pre[fy]];
	return dep[x]<dep[y]?x:y;
}
bool cmp(int x,int y){
	return dep[x]<dep[y];
}
void up(int num){
	sum[num]=sum[lc[num]]+sum[rc[num]];
}
void ins(int old,int x,int y,int l,int r,int &num){
	if(!num){
		num=++id;
		lc[num]=rc[num]=0;
	}
	if(l==r){
		sum[num]=sum[old]+y;
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)ins(lc[old],x,y,l,mid,lc[num]),rc[num]=rc[old];
	else ins(rc[old],x,y,mid+1,r,rc[num]),lc[num]=lc[old];
	up(num);
}
int query(int L,int R,int l,int r,int num){
	if(L<=l&&r<=R)return sum[num];
	int mid=l+r>>1;
	if(R<=mid||!rc[num])return query(L,R,l,mid,lc[num]);
	if(L>mid||!lc[num])return query(L,R,mid+1,r,rc[num]);
	return query(L,R,l,mid,lc[num])+query(L,R,mid+1,r,rc[num]);
}
int main(){
	int _,i,x,y,z,xy,xz,d;
	set<int>::iterator ii;
	for(read(_);_--;){
		read(n);read(m);
		rep(i,1,n)read(w[i]),he[i]=0;
		tot=0;rep(i,2,n){
			read(x);add(x,i);
		}
		rep(i,1,n)son[i]=dad[i]=0;
		dep[1]=1;DFS1(1);cnt=0;DFS2(1);nfd[n+1]=0;
		rep(i,1,n){
			st[bfn[i]=i].clear();
			st[i].insert(0);st[i].insert(n+1);
		}
		sort(bfn+1,bfn+n+1,cmp);
		rt[0]=id=1;md=0;ln=0;
		rep(i,1,n){
			if(dep[x=bfn[i]]>md){
				key[md]=ln;++md;
			}
			rt[++ln]=0;
			ins(rt[ln-1],dfn[x],1,1,n,rt[ln]);
			st[w[x]].insert(dfn[x]);
			y=nfd[*(ii=st[w[x]].upper_bound(dfn[x]))];
			--ii;z=nfd[*(--ii)];
			if(!z&&!y)continue;
			if(y)xy=lca(x,y);
			if(z)xz=lca(x,z);
			if(!y||z&&dep[xz]>dep[xy])xy=xz;
			rt[++ln]=0;
			ins(rt[ln-1],dfn[xy],-1,1,n,rt[ln]);
		}
		key[md]=ln;
		for(ans=0;m--;){
			read(x);read(d);x^=ans;d^=ans;
			printf("%d\n",ans=query(dfn[x],dfn[x]+sz[x]-1,1,n,rt[key[min(dep[x]+d,md)]]));
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值