【JZOJ5512】送你一棵圣诞树

###Description
一棵 n 个点的树, 树根为 1. 一开始每个点上有一个 1…n 的颜色 ci, 不同点颜色可以相同.
现在有 q 次操作, 分为两种类型:

  • 1 u l r: 询问子树 u 中有多少种在 l 到 r 之间的颜色至少出现了一次
  • 2 u c: 将 u 的颜色修改为 c
    强制在线

###Solution
如果只考虑询问一个子树的颜色总数,对于每种颜色,点按dfs序排序,每个点上+1,相邻点lca-1,于是就是经典的子树求和。考虑修改和查询,直接树状数组套线段树即可。

###Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
const int N=1e5+10;
int to[N<<1],nx[N<<1],ls[N],num=0;
int fa[N][17],dep[N],fr[N],en[N],z[N];
multiset<int> cl[N];
typedef multiset<int> :: iterator it;
struct node{
	int l,r,s;
}tr[N<<8];
int rt[N],n,dfn=0,tot=0;
void link(int u,int v){
	to[++num]=v,nx[num]=ls[u],ls[u]=num;
}
int col[N];
void pre(int x){
	fr[x]=++dfn,z[dfn]=x;
	fo(i,1,16) fa[x][i]=fa[fa[x][i-1]][i-1];
	rep(i,x){
		int v=to[i];
		if(v==fa[x][0]) continue;
		fa[v][0]=x,dep[v]=dep[x]+1;
		pre(v);
	}
	en[x]=dfn;
}
int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	fd(i,16,0) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
	if(u==v) return u;
	fd(i,16,0) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
int seg_add(int v,int l,int r,int x,int t){
	if(!v) v=++tot;
	tr[v].s+=t;
	if(l==r) return v;
	int mid=(l+r)>>1;
	x<=mid?tr[v].l=seg_add(tr[v].l,l,mid,x,t):tr[v].r=seg_add(tr[v].r,mid+1,r,x,t);
	return v;
}
int seg_sum(int v,int l,int r,int x,int y){
	if(!v) return 0;
	if(l==x && r==y) return tr[v].s;
	int mid=(l+r)>>1;
	if(y<=mid) return seg_sum(tr[v].l,l,mid,x,y);
	else if(x>mid) return seg_sum(tr[v].r,mid+1,r,x,y);
	else return seg_sum(tr[v].l,l,mid,x,mid)+seg_sum(tr[v].r,mid+1,r,mid+1,y);
}
void add(int x,int p,int t){
	for(;x<=n;x+=x&-x) rt[x]=seg_add(rt[x],1,n,fr[p],t);
}
int sum(int x,int p){
	int tmp=0;
	for(;x;x-=x&-x) tmp+=seg_sum(rt[x],1,n,fr[p],en[p]);
	return tmp;
}
void modify(int x,int t){
	add(col[x],x,t);
	it pos=cl[col[x]].find(fr[x]);
	it p=pos,q=pos;p--,q++;
	if(*p && *q<=n) add(col[x],lca(z[*p],z[*q]),t);
	if(*q<=n) add(col[x],lca(z[*pos],z[*q]),-t);
	if(*p) add(col[x],lca(z[*pos],z[*p]),-t);
}
int main()
{
	freopen("xmastree1.in","r",stdin);
	freopen("xmastree1.out","w",stdout);
	int q,t;
	scanf("%d %d %d",&n,&q,&t);
	fo(i,1,n) scanf("%d",&col[i]);
	fo(i,2,n){
		int u,v;
		scanf("%d %d",&u,&v);
		link(u,v),link(v,u);
	}
	dep[1]=1,pre(1);
	fo(i,1,n) cl[i].insert(0),cl[i].insert(n+1);
	fo(i,1,n) cl[col[z[i]]].insert(i),modify(z[i],1);
	int ans=0;
	while(q--){
		int op,u;
		scanf("%d %d",&op,&u),u^=ans*t;
		if(op==1){
			int l,r;
			scanf("%d %d",&l,&r),l^=ans*t,r^=ans*t;
			printf("%d\n",ans=sum(r,u)-sum(l-1,u));
		}
		else{
			int c;
			scanf("%d",&c),c^=ans*t;
			modify(u,-1),cl[col[u]].erase(fr[u]);
			cl[col[u]=c].insert(fr[u]),modify(u,1);
		}
	}
}

posted @ 2018-01-11 16:07  sadstone  阅读(58)  评论(0编辑  收藏  举报