To_Heart—题解——CF620E
题目
给个链接
题解
题解
这道题目我们首先通过DFN序将树的子树转换为一个区间,则:
1.每次修改整颗子树颜色的操作变换为将一个区间的所有值修改。。
2.每次查询整颗子树颜色种类的操作变为查询一个区间内不同值的个数。
然后这个东西就可以很自然的想到线段树。
这时我们发现颜色不会超过60,所以可以状态压缩成一个在long long范围内的数,最后查询用Lowbit就好了。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
int n,m;
vector<int> v[400005];
int col[400005];
int DFS_col[400005];
int p[400005];
int sz[400005];
int tot=0;
void DFS(int x,int fa){
p[x]=++tot;
DFS_col[p[x]]=col[x];
sz[x]=1;
for (int i=0;i<v[x].size();i++) {
int y=v[x][i];
if(y==fa)
continue;
DFS(y,x);
sz[x]+=sz[y];
}
}
struct zz{
int l,r;
ll col,lazy;
};
struct Tree{
zz t[2000005];
void Push_Up(int p){
t[p].col = t[p<<1].col | t[p<<1|1].col;
}
void Push_Down(int p){
if(!t[p].lazy)
return ;
ll now=t[p].lazy;
t[p<<1].col=t[p<<1].lazy=now;
t[p<<1|1].col=t[p<<1|1].lazy=now;
t[p].lazy=0;
}
void Build_Tree(int p,int l,int r){
t[p].l=l,t[p].r=r;
t[p].lazy=0;
if(l==r){
t[p].col=1ll<<DFS_col[r];
return ;
}
int mid=(l+r)>>1;
Build_Tree(p<<1,l,mid);
Build_Tree(p<<1|1,mid+1,r);
Push_Up(p);
}
void Change_Tree(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){
t[p].col=t[p].lazy=k;
return;
}
int mid=(t[p].l+t[p].r)>>1;
Push_Down(p);
if(l<=mid)
Change_Tree(p<<1,l,r,k);
if(mid+1<=r)
Change_Tree(p<<1|1,l,r,k);
Push_Up(p);
}
ll Find_Tree(int p,int l,int r){
if(l<=t[p].l&&t[p].r<=r){
return t[p].col;
}
int mid=(t[p].l+t[p].r)>>1;
Push_Down(p);
ll now=0;
if(l<=mid)
now|=Find_Tree(p<<1,l,r);
if(mid+1<=r)
now|=Find_Tree(p<<1|1,l,r);
return now;
}
}t;
ll Find(ll x){
ll tot=0;
while(x){
if(x&1)
tot++;
x>>=1;
}
return tot;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%lld",&col[i]);
for(int i=1;i<n;i++){
int x,y;
scanf("%lld%lld",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
DFS(1,-1);
t.Build_Tree(1,1,n);
while(m--){
int op;
scanf("%lld",&op);
if(op&1){
int x,y;
scanf("%lld%lld",&x,&y);
t.Change_Tree(1,p[x],p[x]+sz[x]-1,1ll<<y);
}
else{
int x;
scanf("%lld",&x);
printf("%lld\n",Find(t.Find_Tree(1,p[x],p[x]+sz[x]-1)));
}
}
return 0;
}