题意:给定一棵树,每个节点都有自己的颜色,有两种操作
1. 给定v , c,将节点v及其子树都染成c颜色
2. 给定v ,统计节点v及其子树上的节点一共有几种颜色。
思路:很明显可以就看出来是线段树的操作,但是就是不知道怎么建树。。
应该是按照dfs的顺序对所有节点进行重标号,这样每个节点的子树中节点的标号范围也就是连续的了,然后就能进行正常的线段树操作,需要注意的一点对于颜色我们要进行状压,不然会超时,60种颜色压成long long就好了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define pi acos(-1)
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define rep(i,x,n) for(int i=x;i<n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
using namespace std;
typedef pair<int,int>P;
const int MAXN=1000010;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int n,m,cnt=0;
int col[MAXN],pre[MAXN],post[MAXN];
vector<int>mp[MAXN];
struct node{
int lazy;
ll x;
}tree[MAXN<<1];
void dfs(int u)
{
pre[u]=++cnt;
for(int i=0;i<mp[u].size();i++)
if(!pre[mp[u][i]])
dfs(mp[u][i]);
post[u]=cnt;
}
void pushdown(int rt)
{
if(tree[rt].lazy)
{
tree[rt<<1].x=tree[rt<<1|1].x=(1ll<<(tree[rt].lazy));
tree[rt<<1].lazy=tree[rt<<1|1].lazy=tree[rt].lazy;
tree[rt].lazy=0;
}
}
void pushup(int rt)
{
tree[rt].x=tree[rt<<1].x|tree[rt<<1|1].x;
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
tree[rt].lazy=c;
tree[rt].x=(1ll<<c);
return ;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)
update(L,R,c,lson);
if(R>mid)
update(L,R,c,rson);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
return tree[rt].x;
pushdown(rt);
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid)
ans|=query(L,R,lson);
if(R>mid)
ans|=query(L,R,rson);
return ans;
}
int main()
{
//std::ios::sync_with_stdio(0);
int k,c,u,v,type;
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",col+i);
for(int i=0;i<n-1;i++)
scanf("%d%d",&u,&v),mp[u].push_back(v),mp[v].push_back(u);
dfs(1);
for(int i=1;i<=n;i++)
update(pre[i],pre[i],col[i],1,n,1);
while(m--)
{
scanf("%d",&type);
if(type==1)
{
scanf("%d%d",&k,&c);
update(pre[k],post[k],c,1,n,1);
}
else
{
scanf("%d",&k);
cout<<__builtin_popcountll(query(pre[k],post[k],1,n,1))<<endl;
}
}
return 0;
}