码迷,mamicode.com
首页 > 其他好文 > 详细

染色「SDOI 2011」

时间:2019-12-29 15:13:13      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:tin   main   int start   swa   swap   scanf   这一   while   %s   

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

请你写一个程序依次完成这m个操作。

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

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



题解
看到这种树上操作路径的题大概率就是树链剖分了
先考虑一下 如果是一条链要怎么做
显然 可以用线段树维护 每个区间记录三个值:此区间内的颜色段数量,区间最左边点的颜色,区间最右边点的颜色
那么 一个区间的颜色段数量=左区间颜色段数量+右区间颜色段数量-(左区间最右颜色==右区间最左颜色);最左边点颜色=左区间最左点颜色 最右边点颜色=右区间最右边点颜色
查询也是差不多 把左儿子返回的结果(这里可以返回一个结构体)和右儿子返回的结果按上一行的方法合并一下即可

然而树剖查询一条路径时是有最多log次的线段树区间查询 而不是直接查询一个区间 怎么合并这些查询答案统计出最终答案呢
其实只需要记录一下上一次查询区间的左端点颜色 如果和这一次查询区间的右端点颜色相同 答案-1即可
具体可见代码 统计答案这段还是有点抽搐

ps: 这题线段树区间修改要打懒标记

【代码】

#include <bits/stdc++.h>
using namespace std;

int n, m, a[100005], b1, b2, b3, lst[10];
int head[100005], pre[200005], to[200005], sz;
int fa[100005], d[100005], st[100005], dfn[100005], son[100005], pos[100005], siz[100005], tme;
char tp[5];
struct segtree{
    int l, r, lcol, rcol, cnt, tag;
    segtree() {
        lcol = rcol = tag = -1; l = r = cnt = 0;
    }
} tr[400005];

inline void addedge(int u, int v) {
    pre[++sz] = head[u]; head[u] = sz; to[sz] = v;
}

namespace Segtree{
    inline void pushup(int ind) {
        tr[ind].cnt = tr[ind<<1].cnt + tr[ind<<1|1].cnt - (tr[ind<<1].rcol == tr[ind<<1|1].lcol);
        tr[ind].lcol = tr[ind<<1].lcol; tr[ind].rcol = tr[ind<<1|1].rcol;
    }
    
    inline void pushdown(int ind) {
        if (tr[ind].tag == -1) return;
        tr[ind<<1].cnt = tr[ind<<1|1].cnt = 1; 
        tr[ind<<1].lcol = tr[ind<<1|1].lcol = tr[ind<<1].rcol = tr[ind<<1|1].rcol = tr[ind].tag;
        tr[ind<<1].tag = tr[ind<<1|1].tag = tr[ind].tag; tr[ind].tag = -1;
    }
    
    void build(int ind, int l, int r) {
        tr[ind] = segtree();
        tr[ind].l = l; tr[ind].r = r; tr[ind].tag = -1;
        if (l == r) {
            tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = a[pos[l]];
            return;
        }
        int mid = (l + r) >> 1;
        build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
        pushup(ind);
    }
    
    void update(int ind, int x, int y, int v) {
        int l = tr[ind].l, r = tr[ind].r;
        if (x <= l && r <= y) {
            tr[ind].cnt = 1; tr[ind].lcol = tr[ind].rcol = v; tr[ind].tag = v; return;
        }
        int mid = (l + r) >> 1;
        pushdown(ind);
        if (x <= mid) update(ind<<1, x, y, v);
        if (mid < y) update(ind<<1|1, x, y, v);
        pushup(ind);
    }
    
    inline segtree merge(segtree a, segtree b) {
        segtree ret = segtree();
        ret.cnt = a.cnt + b.cnt - (a.rcol == b.lcol);
        ret.lcol = a.lcol; ret.rcol = b.rcol;
        return ret;
    }
    
    segtree query(int ind, int x, int y) {
        int l = tr[ind].l, r = tr[ind].r;
        if (x <= l && r <= y) {
            return tr[ind];
        }
        int mid = (l + r) >> 1;
        pushdown(ind);
        segtree ret1 = segtree(), ret2 = segtree(); 
        if (x <= mid) ret1 = query(ind<<1, x, y);
        if (mid < y) ret2 = query(ind<<1|1, x, y);
        if (!ret1.cnt) return ret2;
        else if (!ret2.cnt) return ret1;
        else return merge(ret1, ret2);
    }
}

using namespace Segtree;

namespace treechains{
    void dfs1(int x, int f) {
        siz[x] = 1;
        for (int i = head[x]; i; i = pre[i]) {
            int y = to[i];
            if (y == f) continue;
            d[y] = d[x] + 1; fa[y] = x; 
            dfs1(y, x); siz[x] += siz[y];
            if (siz[y] > siz[son[x]]) son[x] = y;
        }
    }
    
    void dfs2(int x, int start) {
        dfn[x] = ++tme; pos[tme] = x; st[x] = start;
        if (son[x]) dfs2(son[x], start);
        for (int i = head[x]; i; i = pre[i]) {
            int y = to[i];
            if (y != fa[x] && y != son[x]) dfs2(y, y);
        }
    }
    
    void change(int x, int y, int z) {
        while (st[x] != st[y]) {
            if (d[st[x]] < d[st[y]]) swap(x, y);
            update(1, dfn[st[x]], dfn[x], z);
            x = fa[st[x]];          
        }
        if (dfn[x] > dfn[y]) swap(x, y);
        update(1, dfn[x], dfn[y], z);
    }
    
    int ask(int x, int y) {
        int ret = 0, o = 0; lst[0] = lst[1] = -1;
                //lst[0]: x~lca这条链上上一次查询的左端点颜色; lst[1]: y~lca这条链上上一次查询的左端点颜色
        segtree tmp = segtree();
        while (st[x] != st[y]) {
            if (d[st[x]] < d[st[y]]) swap(x, y), o ^= 1;
            tmp = query(1, dfn[st[x]], dfn[x]);
            ret += tmp.cnt - (tmp.rcol == lst[o]); lst[o] = tmp.lcol;
            x = fa[st[x]];          
        }
        if (dfn[x] > dfn[y]) swap(x, y), o ^= 1;
        tmp = query(1, dfn[x], dfn[y]);
        if (!o) {
            ret += tmp.cnt - (lst[0] == tmp.lcol) - (lst[1] == tmp.rcol);
        } else {
            ret += tmp.cnt - (lst[1] == tmp.lcol) - (lst[0] == tmp.rcol);
        }
        return ret;
    }
}

using namespace treechains;

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i < n; i++) {
        int x, y; scanf("%d %d", &x, &y);
        addedge(x, y); addedge(y, x);
    }
    dfs1(1, 0); dfs2(1, 1);
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        scanf("%s", tp);
        if (tp[0] == 'C') {
            scanf("%d %d %d", &b1, &b2, &b3);
            change(b1, b2, b3);
        } else {
            scanf("%d %d", &b1, &b2);
            printf("%d\n", ask(b1, b2));
        }
    }
    return 0;
}

染色「SDOI 2011」

标签:tin   main   int start   swa   swap   scanf   这一   while   %s   

原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM26.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!