[BZOJ2243][SDOI2011]染色(树剖+线段树)

这道题真的变态。
先树剖。然后在线段树上维护 1 个标记tag,即区间更新的标记。然后在线段树上维护 3 个值:
col:对应区间左端点的颜色。
cor :对应区间右端点的颜色。
T :对应区间的颜色段数。
合并两个区间,就是将左子区间和右子区间的T值相加,然后如果左子区间的 cor 等于右子区间的 col ,那么这个区间的 T 1
同样,在路径询问中,将一条路径映射成若干个区间后,如果相邻的两个区间对应的路径,它们的相交处(也就是这两条子路径中相邻的一对点)颜色相同,那么累计答案时,要把答案减 1 <script type="math/tex" id="MathJax-Element-12">1</script>。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
inline char get() {
    char c; while ((c = getchar()) != 'C' && c != 'Q');
    return c;
}
const int N = 1e5 + 5;
int n, m, a[N], ecnt, nxt[N << 1], adj[N], go[N << 1], T[N << 2],
tag[N << 2], col[N << 2], cor[N << 2], fa[N], dep[N], sze[N], son[N],
top[N], pos[N], idx[N], tot;
struct ask_q {int len, col, cor;};
void add_edge(int u, int v) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
void down(int p) {
    if (tag[p] != -1) {
        tag[p2] = tag[p]; tag[p3] = tag[p];
        tag[p] = -1;
    }
}
void calc(int p) {
    col[p] = tag[p2] == -1 ? col[p2] : tag[p2];
    cor[p] = tag[p3] == -1 ? cor[p3] : tag[p3];
    int lp = tag[p2] == -1 ? T[p2] : 1,
        rp = tag[p3] == -1 ? T[p3] : 1,
        li = tag[p2] == -1 ? cor[p2] : tag[p2],
        ri = tag[p3] == -1 ? col[p3] : tag[p3];
    T[p] = lp + rp - (li == ri);
}
void build(int l, int r, int p) {
    if (l == r) {
        T[p] = 1; col[p] = cor[p] = a[idx[l]];
        tag[p] = -1; return;
    }
    int mid = l + r >> 1;
    build(l, mid, p2); build(mid + 1, r, p3);
    tag[p] = -1; col[p] = col[p2]; cor[p] = cor[p3];
    T[p] = T[p2] + T[p3] - (cor[p2] == col[p3]);
}
void change(int l, int r, int s, int e, int v, int p) {
    if (l == s && r == e) return (void) (tag[p] = v);
    int mid = l + r >> 1; down(p);
    if (e <= mid) change(l, mid, s, e, v, p2);
    else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
    else change(l, mid, s, mid, v, p2),
        change(mid + 1, r, mid + 1, e, v, p3);
    calc(p);
}
ask_q ask(int l, int r, int s, int e, int p) {
    if (l == s && r == e) {
        ask_q rw;
        rw.len = tag[p] == -1 ? T[p] : 1;
        rw.col = tag[p] == -1 ? col[p] : tag[p];
        rw.cor = tag[p] == -1 ? cor[p] : tag[p];
        return rw;
    }
    int mid = l + r >> 1; down(p); ask_q z, t, uv;
    if (e <= mid) {
        z = ask(l, mid, s, e, p2);
        uv.len = z.len; uv.col = z.col; uv.cor = z.cor;
    }
    else if (s >= mid + 1) {
        z = ask(mid + 1, r, s, e, p3);
        uv.len = z.len; uv.col = z.col; uv.cor = z.cor;
    }
    else {
        z = ask(l, mid, s, mid, p2); t = ask(mid + 1, r, mid + 1, e, p3);
        uv.len = z.len + t.len - ((tag[p2] == -1 ? cor[p2] : tag[p2])
            == (tag[p3] == -1 ? col[p3] : tag[p3]));
        uv.col = z.col; uv.cor = t.cor;
    }
    calc(p); return uv;
}
void dfs1(int u, int fu) {
    sze[u] = 1; dep[u] = dep[fu] + 1; fa[u] = fu;
    for (int e = adj[u], v; e; e = nxt[e])
        if ((v = go[e]) != fu) {
            dfs1(v, u); sze[u] += sze[v];
            if (sze[v] > sze[son[u]]) son[u] = v;
        }
}
void dfs2(int u, int fu) {
    if (son[u]) {
        idx[pos[son[u]] = ++tot] = son[u];
        top[son[u]] = top[u];
        dfs2(son[u], u);
    }
    for (int e = adj[u], v; e; e = nxt[e])
        if ((v = go[e]) != fa[u] && v != son[u])
            idx[pos[v] = ++tot] = v, top[v] = v, dfs2(v, u);
}
void path_change(int u, int v, int w) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        change(1, n, pos[top[u]], pos[u], w, 1); u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v); change(1, n, pos[u], pos[v], w, 1);
}
int path_query(int u, int v) {
    int res = 0, cu = -1, cv = -1; ask_q rr;
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v), swap(cu, cv);
        rr = ask(1, n, pos[top[u]], pos[u], 1); res += rr.len;
        if (rr.cor == cu) res--; cu = rr.col;
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v), swap(cu, cv);
    rr = ask(1, n, pos[u], pos[v], 1);
    return res + rr.len - (rr.col == cu) - (rr.cor == cv);
}
int main() {
    int i, x, y, z; char c;
    n = read(); m = read();
    for (i = 1; i <= n; i++) a[i] = read();
    for (i = 1; i < n; i++) {
        x = read(); y = read();
        add_edge(x, y); add_edge(y, x);
    }
    dfs1(1, 0); tot = top[1] = pos[1] = idx[1] = 1;
    dfs2(1, 0); build(1, n, 1);
    while (m--) {
        c = get(); x = read(); y = read();
        if (c == 'C') z = read(), path_change(x, y, z);
        else printf("%d\n", path_query(x, y));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值