CodeForces 620E New Year Tree(线段树)

题意:给出一棵树,每个节点有一个颜色,现在有两种操作,一种是将一棵子树所有节点置为一种颜色,另一种是求一棵子树内的结点颜色数量。

思路:首先处理出每个节点的dfs序转化为线性区间上的问题。然后剩下就是一个裸的线段树问题,用每一个二进制位代表一种颜色,然后结点的权值表示当前区间有多少种颜色,区将合并只需要或运算即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 500000;

//树相关部分
int n, m, dfs_clock;
vector<int> G[MAXN];
int node[MAXN], tim[MAXN][2];
void dfs(int cur, int fa) {
	tim[cur][0] = ++dfs_clock;
	for(int i = 0; i < G[cur].size(); i++) {
		int u = G[cur][i];
		if(u == fa) continue;
		dfs(u, cur);
	}
	tim[cur][1] = dfs_clock;
}

//线段树部分 
LL col[MAXN<<2], setv[MAXN<<2];
int cal_col(LL col) {
	int cnt = 0;
	for(int i = 0; i < 63; i++) {
		if((1LL<<i) & col) cnt++; 
	}
	return cnt;
}
void pushdown(int o) {
	if(setv[o] > 0) {
		setv[o<<1] = setv[(o<<1)|1] = setv[o];
		setv[o] = 0;
	}
}
void maintain(int o, int L, int R) {
	col[o] = 0;
	if(R > L) col[o] = col[o<<1] | col[(o<<1)|1];
	if(setv[o] > 0) col[o] = setv[o];
}
void update(int o, int L, int R, int ql, int qr, int c) {
	if(ql<=L && qr>=R) setv[o] = 1LL<<c;
	else {
		pushdown(o);
		int M = (L+R) >> 1;
		if(ql<=M) update(o<<1, L, M, ql, qr, c); else maintain(o<<1, L, M);
		if(qr>M) update((o<<1)|1, M+1, R, ql, qr, c); else maintain((o<<1)|1, M+1, R);
	} 
	maintain(o, L, R);
}
LL query(int o, int L, int R, int ql, int qr) {
	if(setv[o] > 0) return setv[o];
	else if(ql<=L && qr>=R) return col[o];
	int M = (L+R) >> 1;
	LL ans = 0;
	if(ql <= M) ans |= query(o<<1, L, M, ql, qr);
	if(qr > M) ans |= query((o<<1)|1, M+1, R, ql, qr);
	return ans;
}

 
int main() {
    //freopen("input.txt", "r", stdin);
	dfs_clock = 0;
	cin >> n >> m;
	for(int i = 1; i <= n; i++) scanf("%d", &node[i]);
	for(int i = 1; i < n; i++) {
		int u, v;
		scanf("%d%d", &u, &v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1, -1);
	for(int i = 1; i <= n; i++) {
		update(1, 1, n, tim[i][0], tim[i][0], node[i]);
	}
	for(int i = 1; i <= m; i++) {
		int op, u, v;
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d%d", &u, &v);
			update(1, 1, n, tim[u][0], tim[u][1], v);
		}
		else {
			scanf("%d", &u);
			LL ans = query(1, 1, n, tim[u][0], tim[u][1]);
			printf("%d\n", cal_col(ans));
		}
	}
    return 0;
}

















  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值