P2486 [SDOI2011]染色

P2486

很经典的题~

思路: 线段树染色+"熟练"剖分(某些出题人总是喜欢把序列上的题加个树链剖分搞到树上去)

先想一想序列上怎么做吧

线段树是个好东西

每个节点维护三个信息: ls: 左端点的颜色 rs: 右端点的颜色 cnt: [l, r] 中共有几个颜色段

合并?

fa.cnt = son1.cnt + son2.cnt - [son1.rs == son2.ls]

fa.ls = son1.ls , fa.rs = son2.rs

爹的左端点颜色就是左儿子的左端点颜色, 右端点亦然

如果左儿子与右儿子相接的颜色相同, 那么等于左儿子块数加右儿子块数-1(中间两个块会合成一个)

否则直接加就行啦

修改时要打标记 记录有没有被覆盖

回到树上问题时要特别注意的是询问

因为询问时有swap的操作, 将k记录x,y的顺序, 即相当于(x, y) 还是(y, x)

如果是(y, x), 最后还要反回来才能进行合并

在跳重链时, 总是将链接在它的左边, 最后将a左右儿子反一下再与b合并即可

#include<iostream>
#include<cstdio>
#include<cstdio>
#define ll long long
using namespace std;
const int N = 105000*4;
int fa[N], id[N], siz[N];
int num, dep[N], son[N];
int w[N], wt[N], Top[N];
int h[N], ne[N], to[N];
int tot;
inline void add(int x,int y) {
	ne[++tot] = h[x], h[x] = tot;
	to[tot] = y;
}
void dfs1(int x,int f) {
	fa[x] = f;
	siz[x] = 1, dep[x] = dep[f] + 1;
	for (int i = h[x]; i ;i = ne[i]) {
		int y = to[i];
		if (y == f) continue;
		dfs1(y, x);
		siz[x] += siz[y];
		if (siz[y] > siz[son[x]]) son[x] = y;
	}
}
void dfs2(int x,int topf) {
	id[x] = ++num; wt[num] = w[x];
	Top[x] = topf;
	if (!son[x]) return;
	dfs2(son[x], topf);
	for (int i = h[x]; i; i = ne[i]) {
		int y = to[i];
		if (y == fa[x] || y == son[x]) continue;
		dfs2(y, y);
	}
}
int n, m;
int L[N], R[N], cnt[N], ls[N], rs[N];
int tag[N];
#define p1 p << 1
#define p2 p << 1 | 1

struct node{
	int cnt, ls, rs;
};
void update(node &fa,node i,node j) {
	fa.cnt = i.cnt + j.cnt - (i.rs == j.ls);
	fa.ls = i.ls, fa.rs = j.rs;
}

void build(int l,int r,int p) {
	L[p] = l, R[p] = r;
	if (l == r) {
		cnt[p] = 1, ls[p] = rs[p] = wt[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, p1);
	build(mid+1, r, p2);
	cnt[p] = cnt[p1] + cnt[p2] - (rs[p1] == ls[p2]);
	ls[p] = ls[p1], rs[p] = rs[p2];
}

void spread(int p) {
	if (tag[p]) {
		cnt[p1] = cnt[p2] = 1;
		tag[p1] = tag[p2] = tag[p];
		ls[p1] = ls[p2] = rs[p1] = rs[p2] = tag[p];
		tag[p] = 0;
	}
}

void change(int l,int r,int p,int c) {
	if (L[p] >= l && R[p] <= r) {
		cnt[p] = 1, tag[p] = c;
		ls[p] = rs[p] = c;
		return;
	}
	spread(p);
	if (R[p1] >= l) change(l, r, p1, c);
	if (L[p2] <= r) change(l, r, p2, c);
	cnt[p] = cnt[p1] + cnt[p2] - (rs[p1] == ls[p2]);
	ls[p] = ls[p1], rs[p] = rs[p2];
}

node ask(int l,int r,int p) {
	if (L[p] >= l && R[p] <= r) return (node){cnt[p], ls[p], rs[p]};
	spread(p);
	node i;
	if (R[p1] < l) return ask(l, r, p2);
	if (L[p2] > r) return ask(l, r, p1);
	update(i, ask(l, r, p1), ask(l, r, p2));
	return i;
}
	
void change_e(int x,int y,int c) {
	while (Top[x] != Top[y]) {
		if (dep[Top[x]] < dep[Top[y]]) swap(x, y);
		change(id[Top[x]], id[x], 1, c);
		x = fa[Top[x]];
	}
	if (dep[x] < dep[y]) swap(x, y);
	change(id[y], id[x], 1, c);
}

int sum(int x,int y) {
	node ans, a, b;
	ans = a = b = (node){0,0,0};
	int k = 1;
	while (Top[x] != Top[y]) {
		if (dep[Top[x]] < dep[Top[y]]) swap(x, y), swap(a, b), k ^= 1;
		if (a.cnt == 0) a = ask(id[Top[x]], id[x], 1);
		else update(a, ask(id[Top[x]], id[x], 1), a);
		x = fa[Top[x]];
	}
	if (dep[x] < dep[y]) swap(x, y), swap(a, b);
	if (a.cnt == 0) a = ask(id[y], id[x], 1);
	else update(a, ask(id[y], id[x], 1), a);
	if (b.cnt == 0) return a.cnt;
	if (a.cnt == 0) return b.cnt;
	if (!k) swap(a, b); // 将a, b恢复正常顺序
	swap(a.ls, a.rs); //将a左右儿子换位
	update(ans, a, b);
	return ans.cnt;
}
	
		
char s[5];
ll a, b, c;

template <typename T> 
void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (;!isdigit(c); c = getchar()) if (c == '-') f = -1;
	for (;isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
	x *= f;
}
int main() {
	read(n), read(m);
	for (int i = 1;i <= n; i++) read(w[i]);
	for (int i = 1;i < n; i++) {
		read(a), read(b);
		add(a, b); add(b, a);
	}
	dfs1(1, 0); 
	dfs2(1, 1);
	build(1, n, 1);
	while (m--) {
		scanf ("%s", s + 1);
		if (s[1] == 'C') {
			read(a), read(b), read(c);
			change_e(a, b, c);
		}
		else {
			read(a), read(b);
			printf ("%d\n", sum(a, b));
		}
	}
	return 0;
}
posted @ 2019-10-10 00:09  Hs-black  阅读(151)  评论(0编辑  收藏  举报