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