BZOJ-2243: [SDOI2011]染色 (树链剖分 入门题 线段树 区间修改查询 维护端点值)

2243: [SDOI2011]染色

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 8608   Solved: 3221
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

Sample Output

3
1
2

HINT

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。


#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct tree{
	int pre, suf, sm, lazy;
}c[maxn * 4];
int pre[maxn], sz[maxn], dep[maxn], son[maxn], top[maxn], id[maxn], val[maxn], a[maxn], tot;
vector<vector<int> >g(maxn);
void dfs1(int x, int fa, int d){
	dep[x] = d;
	sz[x] = 1;
	pre[x] = fa;
	son[x] = 0;
	int cur;
	for(int i = 0; i < g[x].size(); ++i){
		cur = g[x][i];
		if(cur == fa) continue;
		dfs1(cur, x, d + 1);
		sz[x] += sz[cur];
		if(sz[son[x]] < sz[cur]){
			son[x] = cur;
		}
	}
}
void dfs2(int x, int tp){
	top[x] = tp;
	id[x] = ++tot;
	if(son[x]){
		dfs2(son[x], tp);
	}
	int cur;
	for(int i = 0; i < g[x].size(); ++i){
		cur = g[x][i];
		if(cur == pre[x] || cur == son[x]) continue;
		dfs2(cur, cur);
	}
}
void build(int o, int l, int r){
	c[o].lazy = -1;
	if(l == r){
		c[o].pre = c[o].suf = val[l];
		c[o].sm = 1;
		return;
	}
	int mid = l + r >> 1;
	build(o << 1, l, mid);
	build(o << 1 | 1, mid + 1, r);
	c[o].pre = c[o << 1].pre;
	c[o].suf = c[o << 1 | 1].suf;
	c[o].sm = c[o << 1].sm + c[o << 1 | 1].sm - (c[o << 1].suf == c[o << 1 | 1].pre);
}
void pushdown(int o, int l, int r){
	if(c[o].lazy != -1){
		c[o << 1].pre = c[o << 1].suf = c[o].lazy;
		c[o << 1 | 1].pre = c[o << 1 | 1].suf = c[o].lazy;
		c[o << 1].sm = c[o << 1 | 1].sm = 1;
		c[o << 1].lazy = c[o << 1 | 1].lazy = c[o].lazy;
		c[o].lazy = -1;
	}
}
int query(int o, int l, int r, int L, int R, int op){
	if(l >= L && r <= R){
		if(op == 1){
			return c[o].sm;
		}
		if(op == 2){
			return c[o].pre;
		}
		if(op == 3){
			return c[o].suf;
		}
	}
	pushdown(o, l, r);
	int mid = l + r >> 1;
	if(mid < L) return query(o << 1 | 1, mid + 1, r, L, R, op);
	else if(mid >= R) return query(o << 1, l, mid, L, R, op);
	else{
		if(op == 2) return query(o << 1, l, mid, L, R, op);
		if(op == 3) return query(o << 1 | 1, mid + 1, r, L, R, op);
		if(op == 1){
			int ans = query(o << 1, l, mid, L, R, op) + query(o << 1 | 1, mid + 1, r, L, R, op);
			ans -= (query(o << 1, l, mid, L, R, 3) == query(o << 1 | 1, mid + 1, r, L, R, 2));
			return ans;
		}
	}
}
int getsm(int x, int y){
	int tp1 = top[x], tp2 = top[y];
	int ans = 0;
	int cx = -1, cy = -1;   //这两个值用来标记上一段末的颜色,这里值得仔细想想,推敲一下
	while(tp1 != tp2){
		if(dep[tp1] < dep[tp2]){
			swap(tp1, tp2);
			swap(x, y);
			swap(cx, cy);
		}
		ans += query(1, 1, tot, id[tp1], id[x], 1);
		if(query(1, 1, tot, id[tp1], id[x], 3) == cx){
			ans--;
		}
		cx = query(1, 1, tot, id[tp1], id[x], 2);
		x = pre[tp1];
		tp1 = top[x];
	}
	if(dep[x] < dep[y]){
		swap(x, y);
		swap(cx, cy);
	}
	ans += query(1, 1, tot, id[y], id[x], 1);
	ans -= (cx == query(1, 1, tot, id[y], id[x], 3)) + (cy == query(1, 1, tot, id[y], id[x], 2));
	return ans;
}
void update(int o, int l, int r, int L, int R, int v){
	if(l >= L && r <= R){
		c[o].pre = c[o].suf = c[o].lazy = v;
		c[o].sm = 1;
		return;
	}
	int mid = l + r >> 1;
	pushdown(o, l, r);
	if(mid >= L) update(o << 1, l, mid, L, R, v);
	if(mid < R) update(o << 1 | 1, mid + 1, r, L, R, v);
	c[o].pre = c[o << 1].pre;
	c[o].suf = c[o << 1 | 1].suf;
	c[o].sm = c[o << 1].sm + c[o << 1 | 1].sm;
	c[o].sm -= (c[o << 1].suf == c[o << 1 | 1].pre);
}
void change(int x, int y, int v){
	int tp1 = top[x], tp2 = top[y];
	while(tp1 != tp2){
		if(dep[tp1] < dep[tp2]){
			swap(tp1, tp2);
			swap(x, y);
		}
		update(1, 1, tot, id[tp1], id[x], v);
		x = pre[tp1];
		tp1 = top[x];
	}
	if(dep[x] < dep[y]){
		swap(x, y);
	}
	update(1, 1, tot, id[y], id[x], v);
}
int main(){
	int n, m, x, y, v;
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; ++i){
		scanf("%d", &a[i]);
		a[i]++;
	}
	for(int i = 1; i < n; ++i){
		scanf("%d %d", &x, &y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	tot = 0;
	dfs1(1, 0, 1);
	dfs2(1, 1);
	for(int i = 1; i <= n; ++i){
		val[id[i]] = a[i];
	}
	build(1, 1, tot);
	char s[2];
	for(int i = 1; i <= m; ++i){
		scanf("%s", s);
		if(s[0] == 'Q'){
			scanf("%d %d", &x, &y);
			printf("%d\n", getsm(x, y));
		}
		else{
			scanf("%d %d %d", &x, &y, &v);
			v++;
			change(x, y, v);
		}
	}
}

/*
题意:一棵树,1e5个节点,每个节点有初始颜色,用数字表示,1e5次操作,
每次操作要么将两点间路径上的点改成同一给定颜色,要么询问两点之间的
有多少个颜色段。

思路;这题比较明显了,树链剖分+线段树维护区间颜色段的数量,唯一需要主要的
地方是在线段树上分治的时候,注意两个线段前后如果颜色相同不能重复计算。
还有一个要注意的地方是,在树链的区间对应到线段树上的时候,上面的节点即高度小
的节点的标号更小,在线段树中是靠左的,这个在统计颜色的时候要特别注意。
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值