题目链接:
题目链接.
题意:
给出以1为根结点的有n个结点的有根树。有m个操作。
第一种操作:将v结点的子树全部变为c颜色。
第二种操作:输出以v结点为根的子树有多少种不同的颜色。
初始给出每个结点的颜色。
关于dfs序与线段树:
看到将某一个结点的子树全部怎么怎么样,就很容易想到dfs+线段树。
线段树是维护一段区间的问题,而将子树全部怎么怎么样跟将一个区间怎么怎么样是一样的在时间复杂度内难以实现。怎样将树上的问题转化为区间的问题呢,就是将结点重新标号,让这一区间的结点表示为一个子树,也就是说找一个结点的子树只要找一段连续的区间即可。这要用的时间戳的概念,网上关于实现方法的讲解很多,这里不再赘述。
思路:
这道题值得一提的是状态压缩的思想,没有听说过状态压缩的可以去看一下状压DP。
我开始是用暴力一点的方法做的,就是找一个区间的不同的颜色,并用vis数组记录该颜色有没有出现过,一旦区间的颜色被标记,就立即返回,ans++。但这样如果所有结点的颜色都不相同,就相当于遍历了每一个结点,复杂度成了O(n),线段树失去了优势。
下面是正解:
一共有60种颜色,我们想到用状态压缩的思想。60为最多1<<60,刚好long long可以存下。用1<<i位的值表示第i种颜色有没有出现过,0表示没有,1表示出现过。
每个结点就对应的一个二进制状态,表示该结点的子树中的颜色出现的情况。这里的和操作就变成了二进制的与操作了。
11000和00010两种状态的和就是两数相与,就变成11010,表示三种颜色出现过。这样就可以用正常的线段树做了。
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<iostream>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=4e5+10;
vector<int> G[maxn];
int n,m,vis[maxn],change[maxn<<2],in[maxn],out[maxn],time,dfn[maxn];
ll a[maxn],sum[maxn<<2];
/***********dfs序************/
void dfs(int x)
{
//dfn表示映射关系,time时间结点对应的原结点编号,初始化build时要用。
in[x]=++time;dfn[time]=x;
for(int i=0;i<G[x].size();i++)
{
int tmp=G[x][i];
if(vis[tmp]) continue;
vis[tmp]=1;
dfs(tmp);
}
out[x]=time;
}
/***********dfs序************/
/*******线段树**********/
void pushup(int rt)
{
sum[rt]=sum[rt<<1]|sum[rt<<1|1];//这里用到或操作也就是 | 。
}
void pushdown(int rt)
{
if(change[rt]!=-1)
{
int c=change[rt];
change[rt<<1]=change[rt<<1|1]=c;
sum[rt<<1]=1ll<<c;
sum[rt<<1|1]=1ll<<c;
change[rt]=-1;
}
}
void build(int l,int r,int rt)
{
change[rt]=-1;
int m=(r+l)>>1;
if(l==r)
{
sum[rt]=1ll<<a[dfn[l]];
return;
}
build(lson);
build(rson);
pushup(rt);
return;
}
void updata(int ql,int qr,ll c,int l,int r,int rt)
{
int m=(r+l)>>1;
if(ql<=l&&r<=qr)
{
sum[rt]=1ll<<c;
change[rt]=c;
return;
}
pushdown(rt);
if(ql<=m) updata(ql,qr,c,lson);
if(m<qr) updata(ql,qr,c,rson);
pushup(rt);
}
ll ans;
void query(int ql,int qr,int l,int r,int rt)
{
int m=(r+l)>>1;
if(ql<=l&&r<=qr)
{
ans|=sum[rt];
return;
}
pushdown(rt);
if(ql<=m) query(ql,qr,lson);
if(m<qr) query(ql,qr,rson);
return;
}
/*******线段树**********/
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
time=0;
vis[1]=1;
dfs(1);
build(1,n,1);
for(int i=1;i<=m;i++)
{
int t,v;
scanf("%d%d",&t,&v);
if(t==1)
{
ll c;
scanf("%lld",&c);
updata(in[v],out[v],c,1,n,1);
}
else
{
ans=0;
query(in[v],out[v],1,n,1);
int sum=0;
for(int i=1;i<=60;i++)
if(ans&(1ll<<i)) sum++;
printf("%d\n",sum);
}
}
return 0;
}