初见安~最近要到CSP了所以没时间写博客QuQ
这里是传送门:洛谷P2486 染色
题解
很明显是一个树链剖分的裸题——并且需要借助线段树。我们考虑如何用线段树维护区间内的颜色数信息。
我们可以定义一个flag,表示有flag的区间内的所有点都是flag这个颜色。比如,有线段树上的区间点,我们先把赋成颜色1,后来又给赋上颜色2,那么我们最后只认在区间的标记即可。如果顺序反过来,我们在找区间的时候就可以把沿路的标记down下去。当然,多个颜色flag就是0.
加上树剖,我们还需要管的就是:如果两个区间分别有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——