染色「SDOI 2011」

【题目描述】
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

请你写一个程序依次完成这m个操作。

【输入格式】
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面n-1行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面m行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

【输出格式】
对于每个询问操作,输出一行答案。



题解
看到这种树上操作路径的题大概率就是树链剖分了
先考虑一下 如果是一条链要怎么做
显然 可以用线段树维护 每个区间记录三个值:此区间内的颜色段数量,区间最左边点的颜色,区间最右边点的颜色
那么 一个区间的颜色段数量=左区间颜色段数量+右区间颜色段数量-(左区间最右颜色==右区间最左颜色);最左边点颜色=左区间最左点颜色 最右边点颜色=右区间最右边点颜色
查询也是差不多 把左儿子返回的结果(这里可以返回一个结构体)和右儿子返回的结果按上一行的方法合并一下即可

然而树剖查询一条路径时是有最多log次的线段树区间查询 而不是直接查询一个区间 怎么合并这些查询答案统计出最终答案呢
其实只需要记录一下上一次查询区间的左端点颜色 如果和这一次查询区间的右端点颜色相同 答案-1即可
具体可见代码 统计答案这段还是有点抽搐

ps: 这题线段树区间修改要打懒标记

【代码】

#include <bits/stdc++.h>
using namespace std;

int n, m, a[100005], b1, b2, b3, lst[10];
int head[100005], pre[200005], to[200005], sz;
int fa[100005], d[100005], st[100005], dfn[100005], son[100005], pos[100005], siz[100005], tme;
char tp[5];
struct segtree{
	int l, r, lcol, rcol, cnt, tag;
	segtree() {
		lcol = rcol = tag = -1; l = r = cnt = 0;
	}
} tr[400005];

inline void addedge(int u, int v) {
	pre[++sz] = head[u]; head[u] = sz; to[sz] = v;
}

namespace Segtree{
	inline void pushup(int ind) {
		tr[ind].cnt = tr[ind<<1].cnt + tr[ind<<1|1].cnt - (tr[ind<<1].rcol == tr[ind<<1|1].lcol);
		tr[ind].lcol = tr[ind<<1].lcol; tr[ind].rcol = tr[ind<<1|1].rcol;
	}
	
	inline void pushdown(int ind) {
		if (tr[ind].tag == -1) return;
		tr[ind<<1].cnt = tr[ind<<1|1].cnt = 1; 
		tr[ind<<1].lcol = tr[ind<<1|1].lcol = tr[ind<<1].rcol = tr[ind<<1|1].rcol = tr[ind].tag;
		tr[ind<<1].tag = tr[ind<<1|1].tag = tr[ind].tag; tr[ind].tag = -1;
	}
	
	void build(int ind, int l, int r) {
		tr[ind] = segtree();
		tr[ind].l = l; tr[ind].r = r; tr[ind].tag = -1;
		if (l == r) {
			tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = a[pos[l]];
			return;
		}
		int mid = (l + r) >> 1;
		build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
		pushup(ind);
	}
	
	void update(int ind, int x, int y, int v) {
		int l = tr[ind].l, r = tr[ind].r;
		if (x <= l && r <= y) {
			tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = v; tr[ind].tag = v; return;
		}
		int mid = (l + r) >> 1;
		pushdown(ind);
		if (x <= mid) update(ind<<1, x, y, v);
		if (mid < y) update(ind<<1|1, x, y, v);
		pushup(ind);
	}
	
	inline segtree merge(segtree a, segtree b) {
		segtree ret = segtree();
		ret.cnt = a.cnt + b.cnt - (a.rcol == b.lcol);
		ret.lcol = a.lcol; ret.rcol = b.rcol;
		return ret;
	}
	
	segtree query(int ind, int x, int y) {
		int l = tr[ind].l, r = tr[ind].r;
		if (x <= l && r <= y) {
			return tr[ind];
		}
		int mid = (l + r) >> 1;
		pushdown(ind);
		segtree ret1 = segtree(), ret2 = segtree(); 
		if (x <= mid) ret1 = query(ind<<1, x, y);
		if (mid < y) ret2 = query(ind<<1|1, x, y);
		if (!ret1.cnt) return ret2;
		else if (!ret2.cnt) return ret1;
		else return merge(ret1, ret2);
	}
}

using namespace Segtree;

namespace treechains{
	void dfs1(int x, int f) {
		siz[x] = 1;
		for (int i = head[x]; i; i = pre[i]) {
			int y = to[i];
			if (y == f) continue;
			d[y] = d[x] + 1; fa[y] = x; 
			dfs1(y, x); siz[x] += siz[y];
			if (siz[y] > siz[son[x]]) son[x] = y;
		}
	}
	
	void dfs2(int x, int start) {
		dfn[x] = ++tme; pos[tme] = x; st[x] = start;
		if (son[x]) dfs2(son[x], start);
		for (int i = head[x]; i; i = pre[i]) {
			int y = to[i];
			if (y != fa[x] && y != son[x]) dfs2(y, y);
		}
	}
	
	void change(int x, int y, int z) {
		while (st[x] != st[y]) {
			if (d[st[x]] < d[st[y]]) swap(x, y);
			update(1, dfn[st[x]], dfn[x], z);
			x = fa[st[x]];			
		}
		if (dfn[x] > dfn[y]) swap(x, y);
		update(1, dfn[x], dfn[y], z);
	}
	
	int ask(int x, int y) {
		int ret = 0, o = 0; lst[0] = lst[1] = -1;
                //lst[0]: x~lca这条链上上一次查询的左端点颜色; lst[1]: y~lca这条链上上一次查询的左端点颜色
		segtree tmp = segtree();
		while (st[x] != st[y]) {
			if (d[st[x]] < d[st[y]]) swap(x, y), o ^= 1;
			tmp = query(1, dfn[st[x]], dfn[x]);
			ret += tmp.cnt - (tmp.rcol == lst[o]); lst[o] = tmp.lcol;
			x = fa[st[x]];			
		}
		if (dfn[x] > dfn[y]) swap(x, y), o ^= 1;
		tmp = query(1, dfn[x], dfn[y]);
		if (!o) {
			ret += tmp.cnt - (lst[0] == tmp.lcol) - (lst[1] == tmp.rcol);
		} else {
			ret += tmp.cnt - (lst[1] == tmp.lcol) - (lst[0] == tmp.rcol);
		}
		return ret;
	}
}

using namespace treechains;

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = 1; i < n; i++) {
		int x, y; scanf("%d %d", &x, &y);
		addedge(x, y); addedge(y, x);
	}
	dfs1(1, 0); dfs2(1, 1);
	build(1, 1, n);
	for (int i = 1; i <= m; i++) {
		scanf("%s", tp);
		if (tp[0] == 'C') {
			scanf("%d %d %d", &b1, &b2, &b3);
			change(b1, b2, b3);
		} else {
			scanf("%d %d", &b1, &b2);
			printf("%d\n", ask(b1, b2));
		}
	}
	return 0;
}
posted @ 2019-12-29 13:17  AK_DREAM  阅读(257)  评论(0编辑  收藏  举报