题目大意:你有一棵以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
",count(query(1,dfn[x],dfn[x]+siz[x]-1)));
}
}
return 0;
}