BZOJ4771 七彩树

16 篇文章 0 订阅
5 篇文章 0 订阅

很厉害的题

orz了网上的题解,大家似乎都在orz雯舞,那我也赶紧Orz吧,前两天刚在BZOJ把我超了-_-orzorz

然后这题的话,先考虑没有深度的条件

先令每个点的贡献都是1,那么如果有同色点,他们的lca的贡献就要减一,而我们知道取dfs序相邻的两个点的话lca最深,所以令同色点dfs序相邻的点贡献-1,然后答案就是子树和

考虑深度限制,我们对于每一个i,维护只考虑所有深度<=i的点时的答案

考虑现在有深度为i时的答案,那么我们搞个主席树,然后把深度为i+1的点挨个插进来即可,插的时候用set维护一下每个颜色的dfs序,就可以搞了

#include<iostream>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<cstdio>
#include<map>
#include<bitset>
#include<set>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 100010
#define MAXM 10000010
#define ll long long
#define eps 1e-8
#define MOD 1000000007
#define INF 1000000000
struct vec{
	int to;
	int fro;
};
struct data{
	int p;
	int x;
	data(){
		
	}
	data(int _p,int _x){
		p=_p;
		x=_x;
	}
	friend bool operator <(data x,data y){
		return x.p<y.p;
	}
};
vec mp[MAXN];
int tai[MAXN],cnt;
int n,m;
int a[MAXN];
int fa[MAXN][20];
int dep[MAXN];
int siz[MAXN],dfn[MAXN],tim;
int p[MAXN];
int rt[MAXN];
int son[MAXM][2],v[MAXM];
int tot;
int la;
set<data>s[MAXN];
bool cmp(int x,int y){
	return dep[x]<dep[y];
}
inline void be(int x,int y){
	mp[++cnt].to=y;
	mp[cnt].fro=tai[x];
	tai[x]=cnt;
}
int lca(int x,int y){
	int i;
	if(dep[x]<dep[y]){
		swap(x,y);
	}
	for(i=19;~i;i--){
		if(dep[fa[x][i]]>=dep[y]){
			x=fa[x][i];
		}
	}
	for(i=19;~i;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return x==y?x:fa[x][0];
}
void dfs(int x){
	int i,y;
	dfn[x]=++tim;
	siz[x]=1;
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		dfs(y);
		siz[x]+=siz[y];
	}
}
void change(int &x,int xx,int y,int z,int p,int cv){
	x=++tot;
	memcpy(son[x],son[xx],sizeof(son[x]));
	v[x]=v[xx]+cv;
	if(y==z){
		return ;
	}
	int mid=y+z>>1;
	if(p<=mid){
		change(son[x][0],son[xx][0],y,mid,p,cv);
	}else{
		change(son[x][1],son[xx][1],mid+1,z,p,cv);
	}
}
int ask(int x,int y,int z,int l,int r){
	if(!x){
		return 0;
	}
	if(y==l&&z==r){
		return v[x];
	}
	int mid=y+z>>1;
	if(r<=mid){
		return ask(son[x][0],y,mid,l,r);
	}else if(l>mid){
		return ask(son[x][1],mid+1,z,l,r);
	}else{
		return ask(son[x][0],y,mid,l,mid)+ask(son[x][1],mid+1,z,mid+1,r);
	}
}
int main(){
	int i,j,x;
	int tmp;
	scanf("%d",&tmp);
	while(tmp--){
		memset(fa,0,sizeof(fa));
		memset(tai,0,sizeof(tai));
		cnt=0;
		tim=0;
		tot=0;
		la=0;
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++){
			scanf("%d",&a[i]);
			p[i]=i;
		}
		dep[1]=1;
		for(i=2;i<=n;i++){
			scanf("%d",&x);
			be(x,i);
			dep[i]=dep[x]+1;
			for(j=0;x;j++){
				fa[i][j]=x;
				x=fa[x][j];
			}
		}
		dfs(1);
		int wzh=1;
		sort(p+1,p+n+1,cmp);
		for(i=1;i<=n;i++){
			rt[i]=rt[i-1];
			while(dep[p[wzh]]==i&&wzh<=n){
				x=p[wzh];
				change(rt[i],rt[i],1,n,dfn[x],1);
				set<data>::iterator it=s[a[x]].insert(data(dfn[x],x)).first;
				set<data>::iterator itt=it;
				itt++;
				if(it!=s[a[x]].begin()&&itt!=s[a[x]].end()){
					set<data>::iterator tit=it;
					tit--;
					change(rt[i],rt[i],1,n,dfn[lca((*tit).x,(*itt).x)],1);
				}
				if(it!=s[a[x]].begin()){
					set<data>::iterator tit=it;
					tit--;
					change(rt[i],rt[i],1,n,dfn[lca(x,(*tit).x)],-1);
				}
				if(itt!=s[a[x]].end()){
					change(rt[i],rt[i],1,n,dfn[lca(x,(*itt).x)],-1);
				}
				wzh++;
			}
		}
		while(m--){
			scanf("%d%d",&i,&x);
			//*
			i^=la;
			x^=la;
			//*/
			printf("%d\n",la=ask(rt[min(dep[i]+x,n)],1,n,dfn[i],dfn[i]+siz[i]-1));
		}
		for(i=1;i<=n;i++){
			s[i].clear();
		}
	}
	return 0;
}

/*
1
4 4
4 2 3 2 
1 2 1 
3 2
2 2
4 1
4 1

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值