传送门:
http://codeforces.com/problemset/problem/620/E
题意:
一个树,给两种操作,第一种是将一个节点下的所有子树染成一种颜色,第二个操作 是输出一个节点下的子树一共有多少种颜色,多次查询。
这题其实就是裸的线段树区间修改和查询,从颜色只有60种入手,可以用或和的形式存下以每个结点为根的子树种颜色的情况,然后根据时间戳给每个结点标志出区间左右端点,然后套板就ok了,区间修改的时候首先要找到那个区间所对应的结点的编号,然后加标记将其左右两个儿子修改了,可以在查询的过程中继续修改。。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=4e5+9;
ll seg[MAX*4],lazy[MAX*4];
int s[MAX],f[MAX],t,c[MAX],k,v,cc,n,m;
vector<int> g[MAX];
void dfs(int v,int p=-1)
{
s[v]=t++;
for (auto u:g[v]) if (u!=p) dfs(u,v);
f[v]=t;
}
void shift(int id)
{
if (lazy[id]) seg[id*2+1]=seg[id*2+2]=lazy[id*2+1]=lazy[id*2+2]=lazy[id];
lazy[id]=0;
}
void upd(int l,int r,ll val,int s=0,int e=n,int id=0)
{
if (r<=s || e<=l) return ;
if (l<=s && e<=r) {seg[id]=lazy[id]=val; return ;}
int mid=s+e>>1;
shift(id);
upd(l,r,val,s,mid,id*2+1),upd(l,r,val,mid,e,id*2+2);
seg[id]=seg[id*2+1]|seg[id*2+2];
}
ll get(int l,int r,int s=0,int e=n,int id=0)
{
if (r<=s || e<=l) return 0;
if (l<=s && e<=r) return seg[id];
int mid=s+e>>1;
shift(id);
return get(l,r,s,mid,id*2+1)|get(l,r,mid,e,id*2+2);
}
int main()
{
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for (int i=0;i<n;i++) cin>>c[i];
for (int i=0,v,u;i<n-1;i++) cin>>v>>u,v--,u--,g[v].push_back(u),g[u].push_back(v);
dfs(0);
for (int i=0;i<n;i++) upd(s[i],s[i]+1,1LL<<c[i]);
while (m--)
{
cin>>k>>v,v--;
if (k==1) cin>>cc,upd(s[v],f[v],1LL<<cc);
else cout<<__builtin_popcountll(get(s[v],f[v]))<<'\n';
}
}