CF620E New Year Tree

题目大意:你有一棵以1为根的有根树,有n个点,每个节点初始有一个颜色c[i]。

      有两种操作:1 v c 将以v为根的子树中所有点颜色更改为c。

            2 v 查询以v为根的子树中的节点有多少种不同的颜色。

            ( 1<=ci<=60,1<=n,m<=400000)

思路:因为在树上不方便操作,所以我们要将一课树转化成线段,这样方便在线段上进行线段树的操作;一种很经典的方法就是dfs序,一棵树的dfs序有这样一个特点:节点x的子树dfs序是连续的。那么我们就可以在dfs序上进行线段树:我们发现颜色c[i]的数量是小于60的,那么我们就可以对颜色进行二进制压缩,对每个颜色,1表示他出现过,0表示他没出现过,至于线段树怎么合并,只需要将左右两棵树“或 | ”起来,就可以表示一段区间的颜色,最后输出有多少个1即可。2^60在long long范围内,所以可以储存。

代码如下:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int MaxN=500000;
struct edge{int to,next;}e[MaxN*2];
struct tree{int lb,rb,chn;long long sum;}tr[MaxN*2];
int n,m,tot,c[MaxN],last[MaxN];
int id,dfn[MaxN],bel[MaxN],siz[MaxN];
void add_edge(int x,int y){
	e[++tot].to=y;e[tot].next=last[x];last[x]=tot;
	return;
}
void dfs(int x,int f){
	dfn[x]=++id;bel[id]=x;siz[x]=1;
	for(int i=last[x];i;i=e[i].next){
		int u=e[i].to;
		if(u==f) continue;
		dfs(u,x);siz[x]+=siz[u];
	}
	return;
}
void build(int now,int l,int r){
	tr[now].lb=l;tr[now].rb=r;
	if(l==r){tr[now].sum=1ll<<c[bel[l]];return;}//! 1LL<<c[bel[l]]不能1<<c[bel[l]]
	int mid=(l+r)>>1;
	build(now<<1,l,mid);build(now<<1|1,mid+1,r);
	tr[now].sum=tr[now<<1].sum|tr[now<<1|1].sum;return;
}
void downlag(int x){
	if(tr[x].chn) tr[x<<1].sum=tr[x<<1|1].sum=tr[x].sum,tr[x<<1].chn=tr[x<<1|1].chn=1,tr[x].chn=0;
	return;
}
void add(int now,int l,int r,int w){
	if(tr[now].lb>=l&&tr[now].rb<=r){tr[now].sum=1ll<<w;tr[now].chn=1;return;}
	downlag(now);
	int mid=(tr[now].lb+tr[now].rb)>>1;
	if(mid>=l) add(now<<1,l,r,w);if(mid<r) add(now<<1|1,l,r,w);
	tr[now].sum=tr[now<<1].sum|tr[now<<1|1].sum;return;
}
int count(long long x){
	int sum=0;while(x) x-=(x&-x),++sum;
	return sum;
}
long long query(int now,int l,int r){
	if(tr[now].lb>=l&&tr[now].rb<=r) return tr[now].sum;
	downlag(now);
	int mid=(tr[now].lb+tr[now].rb)>>1;long long ans=0;
	if(mid>=l) ans|=query(now<<1,l,r);if(mid<r) ans|=query(now<<1|1,l,r);
	return ans;//!
}
int main(){
	int opt,x,y;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&c[i]);
	for(int i=1;i<n;++i) scanf("%d%d",&x,&y),add_edge(x,y),add_edge(y,x);
	dfs(1,0);build(1,1,n);
	while(m--){
		scanf("%d",&opt);
		if(opt==1){
			scanf("%d%d",&x,&y);
			add(1,dfn[x],dfn[x]+siz[x]-1,y);
		}
		if(opt==2){
			scanf("%d",&x);
			printf("%d\n",count(query(1,dfn[x],dfn[x]+siz[x]-1)));
		}
	}
	return 0;
}

 

  

 

posted @ 2019-07-20 17:54  X_rice  阅读(132)  评论(0编辑  收藏  举报