题意:
给定n个顶点的树,每个点上有一个颜色c(i),
m次操作,操作有两种:
(1,x,y):将x为根的子树所有点颜色置为y
(2,x):查询x为根的子树有多少种不同的颜色
数据范围:n<=4e5,1<=c(i)<=60
解法:
子树问题用dfs序转化为序列问题,
观察到颜色只有60种,显然状压维护颜色数,
这题只有区间覆盖,区间颜色合并,用线段树维护即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PI pair<int,int>
const int maxm=2e6+5;
vector<int>g[maxm];
int L[maxm],R[maxm],rk[maxm],idx;
int col[maxm];
int n,m;
struct Tree{
int a[maxm<<2];
int laz[maxm<<2];
void pp(int node){
a[node]=a[node*2]|a[node*2+1];
}
void pd(int node){
if(laz[node]){
a[node*2]=(1LL<<laz[node]);
a[node*2+1]=(1LL<<laz[node]);
laz[node*2]=laz[node];
laz[node*2+1]=laz[node];
laz[node]=0;
}
}
void build(int l,int r,int node){
if(l==r){
a[node]=(1LL<<col[rk[l]]);
return ;
}
int mid=(l+r)/2;
build(l,mid,node*2);
build(mid+1,r,node*2+1);
pp(node);
}
void update(int st,int ed,int val,int l,int r,int node){
if(st<=l&&ed>=r){
a[node]=(1LL<<val);
laz[node]=val;
return ;
}
pd(node);
int mid=(l+r)/2;
if(st<=mid)update(st,ed,val,l,mid,node*2);
if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
pp(node);
}
int ask(int st,int ed,int l,int r,int node){
if(st<=l&&ed>=r)return a[node];
pd(node);
int mid=(l+r)/2;
int ans=0;
if(st<=mid)ans|=ask(st,ed,l,mid,node*2);
if(ed>mid)ans|=ask(st,ed,mid+1,r,node*2+1);
return ans;
}
}T;
int cal(int x){
int ans=0;
while(x){
ans++;
x&=(x-1);
}
return ans;
}
void dfs(int x,int fa){
L[x]=++idx;
rk[idx]=x;
for(int v:g[x]){
if(v==fa)continue;
dfs(v,x);
}
R[x]=idx;
}
signed main(){
ios::sync_with_stdio(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>col[i];
for(int i=1;i<n;i++){
int a,b;cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,1);
T.build(1,n,1);
while(m--){
int op;cin>>op;
if(op==1){
int x,c;cin>>x>>c;
T.update(L[x],R[x],c,1,n,1);
}else{
int x;cin>>x;
int ans=T.ask(L[x],R[x],1,n,1);
ans=cal(ans);
cout<<ans<<endl;
}
}
return 0;
}