洛谷·[SDOI2011]染色

初见安~最近要到CSP了所以没时间写博客QuQ

这里是传送门:洛谷P2486 染色

题解

很明显是一个树链剖分的裸题——并且需要借助线段树。我们考虑如何用线段树维护区间内的颜色数信息。

我们可以定义一个flag,表示有flag的区间内的所有点都是flag这个颜色。比如,有线段树上的区间点[1,4],[1,2],我们先把[1,2]赋成颜色1,后来又给[1,4]赋上颜色2,那么我们最后只认在区间[1,4]的标记即可。如果顺序反过来,我们在找区间[1,2]的时候就可以把沿路的标记down下去。当然,多个颜色flag就是0.

加上树剖,我们还需要管的就是:如果两个区间分别有a个色段和b个色段,但是拼合起来的时候两端颜色相同,怎么处理?其实我们在树剖网上跳的时候顺手查一下单点的值,也就是看链头和链头的父亲的颜色是否相同即可,相同则a+b-1,否则a+b

没有了。【????

其实真的是个很板子的题目……也就是对于区间的拼合的时候特判的处理比较麻烦。

线段树要处理的值:左端点颜色,右端点颜色,区间段数,flag

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 100005
using namespace std;
typedef long long ll;
int read() {
	int x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

int n, m, val[maxn];

struct edge {int to, nxt;} e[maxn << 1];
int head[maxn], k = 0;
void add(int u, int v) {e[k] = {v, head[u]}; head[u] = k++;}

int size[maxn], dep[maxn], son[maxn], fa[maxn], top[maxn], dfn[maxn], raw[maxn], tot = 0;
void dfs1(int u) {
	size[u] = 1; register int v;
	for(int i = head[u]; ~i; i = e[i].nxt) {
		v = e[i].to; if(v == fa[u]) continue;
		fa[v] = u, dep[v] = dep[u] + 1;
		dfs1(v); size[u] += size[v];
		if(size[son[u]] < size[v]) son[u] = v;
	}
}

void dfs2(int u, int tp) {
	top[u] = tp, dfn[u] = ++tot; raw[tot] = u;
	if(son[u]) dfs2(son[u], tp); register int v;
	for(int i = head[u]; ~i; i = e[i].nxt) {
		v = e[i].to; if(v != fa[u] && v != son[u]) dfs2(v, v);
	}
}

struct node {
	int lc, rc, cnt, flag;
}tree[maxn << 2];

void build(int p, int l, int r) {
	if(l == r) {tree[p].lc = tree[p].rc = val[raw[l]], tree[p].cnt = 1; return;}
	int mid = l + r >> 1;
	build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r);
	tree[p].cnt = tree[p << 1].cnt + tree[p << 1 | 1].cnt; 
    if(tree[p << 1].rc == tree[p << 1 | 1].lc) tree[p].cnt--;//lc和rc就是左右端点的颜色
	tree[p].lc = tree[p << 1].lc, tree[p].rc = tree[p << 1 | 1].rc;
}

void down(int p, int l, int r) {//下传标记
	tree[p << 1].lc = tree[p << 1 | 1].lc = tree[p << 1].rc = tree[p << 1 | 1].rc = tree[p].flag;
	tree[p << 1].flag = tree[p << 1 | 1].flag = tree[p].flag;
	tree[p << 1].cnt = tree[p << 1 | 1].cnt = 1;
	tree[p].flag = 0;
}

int ask(int p, int l, int r, int ls, int rs) {
	if(ls <= l && r <= rs) return tree[p].cnt;
	int mid = l + r >> 1;
	if(tree[p].flag) down(p, l, r);
	if(rs <= mid) return ask(p << 1, l, mid, ls, rs);
	else if(ls > mid) return ask(p << 1 | 1, mid + 1, r, ls, rs);
	else return ask(p << 1, l, mid, ls, rs) + ask(p << 1 | 1, mid + 1, r, ls, rs) - (tree[p << 1].cnt + tree[p << 1 | 1].cnt == tree[p].cnt? 0 : 1);//这里也是看有没有拼合区间
}

int quary(int p, int l, int r, int x) {//单点查询,本来可以和区间查询放到一起用……
	if(tree[p].flag) return tree[p].flag;
	if(l == r) return tree[p].lc;
	int mid = l + r >> 1;
	if(x <= mid) return quary(p << 1, l, mid, x);
	else return quary(p << 1 | 1, mid + 1, r, x);
}

void ask_path(int u, int v) {
	int ans = 0;
	while(top[u] != top[v]) {
		if(dep[top[u]] > dep[top[v]]) swap(u, v);
		ans += ask(1, 1, n, dfn[top[v]], dfn[v]);
		if(quary(1, 1, n, dfn[top[v]]) == quary(1, 1, n, dfn[fa[top[v]]])) ans--;//看是否端点颜色相同
		v = fa[top[v]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	ans += ask(1, 1, n, dfn[u], dfn[v]);
	printf("%d\n", ans);
}

void change(int p, int l, int r, int ls, int rs, int c) {
	if(ls <= l && r <= rs) {tree[p].flag = tree[p].lc = tree[p].rc = c; tree[p].cnt = 1; return;}
	int mid = l + r >> 1;
	if(tree[p].flag) down(p, l, r);
	if(ls <= mid) change(p << 1, l, mid, ls, rs, c);
	if(rs > mid) change(p << 1 | 1, mid + 1, r, ls, rs, c);
	tree[p].cnt = tree[p << 1].cnt + tree[p << 1 | 1].cnt; 
    if(tree[p << 1].rc == tree[p << 1 | 1].lc) tree[p].cnt--;
	tree[p].lc = tree[p << 1].lc, tree[p].rc = tree[p << 1 | 1].rc;
}

void change_path(int u, int v, int c) {
	while(top[u] != top[v]) {
		if(dep[top[u]] > dep[top[v]]) swap(u, v);
		change(1, 1, n, dfn[top[v]], dfn[v], c);
		v = fa[top[v]];
	}
	if(dep[u] > dep[v]) swap(u, v);
	change(1, 1, n, dfn[u], dfn[v], c);
}

signed main() {
	memset(head, -1, sizeof head);
	n = read(), m = read();
	register int u, v, color;
	for(int i = 1; i <= n; i++) val[i] = read();
	for(int i = 1; i < n; i++) u = read(), v = read(), add(u, v), add(v, u);
	
	dfs1(1); dfs2(1, 1);
	build(1, 1, n);
	char op; 
	while(m--) {
		cin >> op; u = read(), v = read();
		if(op == 'Q') ask_path(u, v);
		else color = read(), change_path(u, v, color);
	}
	return 0;
}

就这样了。

迎评:)
——End——

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值