bzoj 2243 染色(树链剖分)

题外话


首先这是个挺裸的题,由于太久没写剖分导致调了好久,前天调了一下午,一直查不到错

昨晚在看春晚的时候突然灵机一动,发现合并的时候出了问题,开电脑把它A掉了= =

感觉自己也蛮拼的

Description


给定一棵有n个节点的无根树和m个操作,操作有2类:

1:将节点a到节点b路径上所有点都染成颜色c

2:询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"、"222"和"1"

Solution


很显然这是要剖分的辣,然后用线段树维护信息。然后我们会发现合并的时候有点蛋疼

它要求颜色段数量,所以我们很自然的想到要维护左右端的颜色信息来进行合并

线段树合并时sum[rt]=sum[ls]+sum[rs]?(rc[ls]==lc[rs])即可

但是我们正常在查询的时候是要在剖分后的树上”跳来跳去的”

所以每次查询的时候我们都需要维护当前左右端颜色信息,然后在”跳”的过程中合并

注意颜色是[0,109]的,所以打标记时要开始赋值为?1

合并的时候想清楚,注意细节就好辣!可以仔细想想或者详情参见代码QvQ

Code

#include <bits/stdc++.h>//树链剖分
using namespace std;
#define ls (rt << 1)
#define rs (rt << 1 | 1)
const int N = 100005, M = N << 2;
int n, m, lcol, rcol, tot, cnt, w[N], q[N], top[N], sz[N], son[N], pre[N], id[N], dep[N], col[N], lc[M], rc[M], sum[M], mark[M], to[M], nxt[M], head[N];
bool vis[N];
inline int readInt() {
    char c;
    while (c = getchar(), c < ‘0‘ || c > ‘9‘);
    int t = c - ‘0‘;
    while (c = getchar(), c >= ‘0‘ && c <= ‘9‘) t = t * 10 + c - ‘0‘;
    return t;
}
void add(int u, int v) {
    to[tot] = v, nxt[tot] = head[u], head[u] = tot++;
    to[tot] = u, nxt[tot] = head[v], head[v] = tot++;
}
void pushdown(int rt) {
    if (~mark[rt]) {
        lc[ls] = lc[rs] = rc[ls] = rc[rs] = mark[ls] = mark[rs] = mark[rt];
        sum[ls] = sum[rs] = 1;
        mark[rt] = -1;
    }
}
void pushup(int rt) {
    lc[rt] = lc[ls], rc[rt] = rc[rs];
    sum[rt] = sum[ls] + sum[rs] - (rc[ls] == lc[rs]);
}
void build(int rt, int l, int r) {
    mark[rt] = -1;
    if (l == r) {
        lc[rt] = rc[rt] = w[l];
        sum[rt] = 1;
        return ;
    }
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    pushup(rt);
}
void change(int rt, int l, int r, int L, int R, int c) {
    if (L <= l && R >= r) {
        lc[rt] = rc[rt] = mark[rt] = c;
        sum[rt] = 1;
        return;
    }
    pushdown(rt);
    int mid = l + r >> 1;
    if (L <= mid)   change(ls, l, mid, L, R, c);
    if (R > mid)    change(rs, mid + 1, r, L, R, c);
    pushup(rt);
}
int ask(int rt, int l, int r, int L, int R) {
    if (L == l) lcol = lc[rt];
    if (R == r) rcol = rc[rt];
    if (L <= l && R >= r)   return sum[rt];
    pushdown(rt);
    int mid = l + r >> 1, t = 0, t1 = -1, t2 = -1;
    if (L <= mid)   t += ask(ls, l, mid, L, R), t1 = rc[ls];
    if (R > mid)    t += ask(rs, mid + 1, r, L, R), t2 = lc[rs];
    t -= (t1 == t2 && ~t1);
    return t;
}
void work(int a, int b, int c) {
    while (top[a] != top[b]) {
        if (dep[top[a]] < dep[top[b]])  swap(a, b);
        change(1, 1, n, id[top[a]], id[a], c);
        a = pre[top[a]];
    }
    if (dep[a] < dep[b])    swap(a, b);
    change(1, 1, n, id[b], id[a], c);
}
int work2(int a, int b) {
    int t = 0, t1 = -1, t2 = -1;
    while (top[a] != top[b]) {
        if (dep[top[a]] < dep[top[b]])  swap(a, b), swap(t1, t2);
        t += ask(1, 1, n, id[top[a]], id[a]);
        t -= (t1 == rcol && ~t1);
        t1 = lcol;
        a = pre[top[a]];
    }
    if (dep[a] < dep[b])    swap(a, b), swap(t1, t2);
    t += ask(1, 1, n, id[b], id[a]);
    t -= ((t1 == rcol && ~t1) + (t2 == lcol && ~t2));
    return t;
}
void gao() {
    int r = 0;
    vis[dep[1] = q[0] = 1] = 1;
    for (int i = 0; i <= r; ++i)
        for (int j = head[q[i]]; ~j; j = nxt[j])
            if (!vis[to[j]]){
                vis[to[j]] = 1;
                dep[q[++r] = to[j]] = dep[q[i]] + 1;
                pre[q[r]] = q[i];
            }
    for (int i = r; i >= 0; --i) {
        sz[pre[q[i]]] += ++sz[q[i]];
        if (sz[son[pre[q[i]]]] < sz[q[i]])  son[pre[q[i]]] = q[i];
    }
    for (int i = 0; i <= r; ++i)
        if (!top[q[i]]) {
            for (int j = q[i]; j; j = son[j]) {
                top[j] = q[i];
                w[id[j] = ++cnt] = col[j];
            }
        }
    build(1, 1, n);
}
int main() {
    n = readInt(), m = readInt();
    memset(head, -1, sizeof(head));
    for (int i = 1; i <= n; ++i)    col[i] = readInt();
    for (int i = 1, x, y; i < n; ++i) {
        x = readInt(), y = readInt();
        add(x, y);
    }
    gao();
    while (m--) {
        char s[5];
        scanf("%s", s);
        int a, b, c;
        if (s[0] == ‘C‘) {
            a = readInt(), b = readInt(), c = readInt();
            work(a, b, c);
        }
        else {
            a = readInt(), b = readInt();
            printf("%d\n", work2(a, b));
        }
    }
    return 0;
}
时间: 2024-08-02 07:02:22

bzoj 2243 染色(树链剖分)的相关文章

BZOJ 2243 染色 | 树链剖分模板题进阶版

BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上还是原树上,把两个区间的信息合并的时候,要注意中间相邻两个颜色是否相同. 这代码好长啊啊啊啊 幸好一次过了不然我估计永远也De不出来 #include <cstdio> #include <cstring> #include <algorithm> using namesp

hysbz 2243 染色(树链剖分)

题目链接:hysbz 2243 染色 题目大意:略. 解题思路:树链剖分+线段树的区间合并,但是区间合并比较简单,节点只要记录左右端点的颜色即可. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1e5 + 5; int N, M, ne, val[maxn], first[maxn], jump[maxn * 2]; int

HYSBZ 2243 染色 树链剖分 线段树

这题对最简单的树链剖分做了一些变化,在链的转移过程中要考虑前后链相邻节点颜色是否相同. 对于线段树,只要维护三个值,左端点颜色,右端点颜色还有区间颜色总数就好了. #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <set> #include <bitset> #include <queue> #include

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

BZOJ 3083 遥远的国度 树链剖分

3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 797  Solved: 181[Submit][Status] Description 描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀. 问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

[bzoj 2243]: [SDOI2011]染色 [树链剖分][线段树]

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

bzoj-2243 2243: [SDOI2011]染色(树链剖分)

题目链接: 2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6267  Solved: 2291 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含

2243: [SDOI2011]染色(树链剖分+线段树)

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 8400  Solved: 3150[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完