CF620E New Year Tree

题面翻译

  • 给出一棵 \(n\) 个节点的树,根节点为 \(1\)。每个节点上有一种颜色 \(c_i\)\(m\) 次操作。操作有两种:
    1. 1 u c:将以 \(u\) 为根的子树上的所有节点的颜色改为 \(c\)
    2. 2 u:询问以 \(u\) 为根的子树上的所有节点的颜色数量。
  • \(1\le n,m\le 4\times 10^5\)\(1\le c_i,c\le 60\)

样例 #1

样例输入 #1

7 10
1 1 1 1 1 1 1
1 2
1 3
1 4
3 5
3 6
3 7
1 3 2
2 1
1 4 3
2 1
1 2 5
2 1
1 6 4
2 1
2 2
2 3

样例输出 #1

2
3
4
5
1
2

样例 #2

样例输入 #2

23 30
1 2 2 6 5 3 2 1 1 1 2 4 5 3 4 4 3 3 3 3 3 4 6
1 2
1 3
1 4
2 5
2 6
3 7
3 8
4 9
4 10
4 11
6 12
6 13
7 14
7 15
7 16
8 17
8 18
10 19
10 20
10 21
11 22
11 23
2 1
2 5
2 6
2 7
2 8
2 9
2 10
2 11
2 4
1 12 1
1 13 1
1 14 1
1 15 1
1 16 1
1 17 1
1 18 1
1 19 1
1 20 1
1 21 1
1 22 1
1 23 1
2 1
2 5
2 6
2 7
2 8
2 9
2 10
2 11
2 4

样例输出 #2

6
1
3
3
2
1
2
3
5
5
1
2
2
1
1
1
2
3

分析

考虑到 \(c<60\)\(2^{60}\) long long 刚好装得下。那么把每一个 \(c\) 转为一个二进制位 \(c\)\(1\) 的数,表示区间内有此颜色存在。

对于整个树拍成一个 dfn 序,对于查找一个子树就是子树根节点进出时间范围内的所有节点。

区间总数符合结合律,考虑用线段树维护,记得 pushup 操作是异或操作(因为二进制)

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
	return x*f;
}
struct node{
	int v,nxt;
}e[400005<<1];
int tot,tim,h[400005<<1],in[400005],out[400005],pos[400005<<1];
int a[400005],t[400005<<2],tag[400005<<2];
int n,m;
int lowbit(int x){return x&(-x);}
void dfs(int x,int fa){
	pos[in[x]=++tim]=x;
	for(int i=h[x];i;i=e[i].nxt)if(e[i].v^fa)dfs(e[i].v,x);
	out[x]=tim;
}
void pushup(int num){t[num]=t[num<<1]|t[num<<1|1];}
void pushdown(int num,int l,int r){
	if(tag[num]){
		tag[num<<1]=tag[num<<1|1]=t[num<<1]=t[num<<1|1]=tag[num];
		tag[num]=0;
	}
}
void add_edge(int u,int v){e[++tot].v=v,e[tot].nxt=h[u],h[u]=tot;}
void build(int l,int r,int num){
	if(l==r){
		t[num]=1ll<<a[pos[l]];
		return;
	}
	int mid=l+r>>1;
	build(l,mid,num<<1),build(mid+1,r,num<<1|1);
	pushup(num);
}
void update(int l,int r,int num,int x,int y,int c){
	if(x>r||y<l)return;
	if(x<=l&&r<=y){
		t[num]=tag[num]=1ll<<c;
		return;
	}
	pushdown(num,l,r);
	int mid=l+r>>1;
	update(l,mid,num<<1,x,y,c),update(mid+1,r,num<<1|1,x,y,c);
	pushup(num);
}
int query(int l,int r,int num,int x,int y){
	if(x>r||y<l)return 0;
	if(x<=l&&r<=y)return t[num];
	pushdown(num,l,r);
	int mid=l+r>>1;
	return query(l,mid,num<<1,x,y)|query(mid+1,r,num<<1|1,x,y);
}
int get(int x,int ans=0){
	for(int i=x;i>0;i-=lowbit(i))++ans;
	return ans;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1,x,y;i<n;i++){
		x=read(),y=read();
		add_edge(x,y),add_edge(y,x);
	}
	dfs(1,0);build(1,n,1);
	for(int i=1,opt,u,c;i<=m;i++){
		opt=read();
		if(opt==1){
			u=read(),c=read();
			update(1,n,1,in[u],out[u],c);
		}else{
			u=read();
			printf("%lld\n",get(query(1,n,1,in[u],out[u])));
		}
	}
	return 0;
}
posted @ 2023-06-24 13:57  alex_liu09  阅读(13)  评论(0)    收藏  举报