[Sdoi2014]旅行 树链剖分+动态开点

Description
给你一棵树,每个点有一个权值s[i],一个颜色c[i],有四种操作:
CC x c:点x的颜色改成c;
CW x w:点x的权值调整为w;
QS x y:一位旅行者从城市x出发,到城市y,并记下了途中颜色相同的点的权值总和;
QM x y:一位旅行者从城市x出发,到城市y,并记下了途中颜色相同的点的权值最大值。


Sample Input
5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4


Sample Output
8
9
11
3


这题其实有一点水。。。
你将每个颜色建一个线段树,然后动态开点搞一下,树链剖分维护一下。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
const int maxn = 110000;

struct node {
    int x, y, next;
} e[2 * maxn]; int len, last[maxn];
struct trnode {
    int lc, rc, sum, max;
} t[20 * maxn]; int cnt, rt[maxn];
int id, tot[maxn], fa[maxn], son[maxn], dep[maxn], top[maxn], ys[maxn];
int w[maxn], cc[maxn];
char ss[11];

void ins(int x, int y) {
    e[++len].x = x; e[len].y = y;
    e[len].next = last[x]; last[x] = len;
}

void pre_tree_node(int x) {
    son[x] = 0; tot[x] = 1;
    for(int k = last[x]; k; k = e[k].next) {
        int y = e[k].y;
        if(y != fa[x]) {
            fa[y] = x;
            dep[y] = dep[x] + 1;
            pre_tree_node(y);
            tot[x] += tot[y];
            if(tot[son[x]] < tot[y]) son[x] = y;
        }
    }
}

void pre_tree_edge(int x, int tp) {
    top[x] = tp; ys[x] = ++id;
    if(son[x]) pre_tree_edge(son[x], tp);
    for(int k = last[x]; k; k = e[k].next) {
        int y = e[k].y;
        if(y != fa[x] && y != son[x]) pre_tree_edge(y, y);
    }
}

void Link(int &u, int l, int r, int p, int c) {
    if(!u) u = ++cnt;
    t[u].sum += c; t[u].max = _max(t[u].max, c);
    if(l == r) return ;
    int mid = (l + r) / 2;
    if(p <= mid) Link(t[u].lc, l, mid, p, c);
    else Link(t[u].rc, mid + 1, r, p, c);
}

void del(int u, int l, int r, int p) {
    if(l == r) {
        t[u].sum = t[u].max = 0;
        return ;
    }
    int mid = (l + r) / 2;
    if(p <= mid) del(t[u].lc, l, mid, p);
    else del(t[u].rc, mid + 1, r, p);
    t[u].sum = t[t[u].lc].sum + t[t[u].rc].sum;
    t[u].max = _max(t[t[u].lc].max, t[t[u].rc].max);
}

int findsum(int u, int l, int r, int ll, int rr) {
    if(!u) return 0;
    if(l == ll && r == rr) return t[u].sum;
    int mid = (l + r) / 2;
    if(rr <= mid) return findsum(t[u].lc, l, mid, ll, rr);
    else if(ll > mid) return findsum(t[u].rc, mid + 1, r, ll, rr);
    else return findsum(t[u].lc, l, mid, ll, mid) + findsum(t[u].rc, mid + 1, r, mid + 1, rr);
}

int findmax(int u, int l, int r, int ll, int rr) {
    if(!u) return 0;
    if(l == ll && r == rr) return t[u].max;
    int mid = (l + r) / 2;
    if(rr <= mid) return findmax(t[u].lc, l, mid, ll, rr);
    else if(ll > mid) return findmax(t[u].rc, mid + 1, r, ll, rr);
    else return _max(findmax(t[u].lc, l, mid, ll, mid), findmax(t[u].rc, mid + 1, r, mid + 1, rr));
}

int solve1(int x, int y, int c) {
    int tx = top[x], ty = top[y], ans = 0;
    while(tx != ty) {
        if(dep[tx] > dep[ty]) swap(tx, ty), swap(x, y);
        ans += findsum(rt[c], 1, id, ys[ty], ys[y]);
        y = fa[ty]; ty = top[y];
    }
    if(dep[x] > dep[y]) swap(x, y);
    ans += findsum(rt[c], 1, id, ys[x], ys[y]);
    return ans;
}

int solve2(int x, int y, int c) {
    int tx = top[x], ty = top[y], ans = 0;
    while(tx != ty) {
        if(dep[tx] > dep[ty]) swap(tx, ty), swap(x, y);
        ans = _max(ans, findmax(rt[c], 1, id, ys[ty], ys[y]));
        y = fa[ty]; ty = top[y];
    }
    if(dep[x] > dep[y]) swap(x, y);
    ans = _max(ans, findmax(rt[c], 1, id, ys[x], ys[y]));
    return ans;
}

int main() {
    int n, m; scanf("%d%d", &n, &m);
    pre_tree_node(1);
    for(int i = 1; i <= n; i++) scanf("%d%d", &w[i], &cc[i]);
    for(int i = 1; i < n; i++) {
        int x, y; scanf("%d%d", &x, &y);
        ins(x, y); ins(y, x);
    }
    pre_tree_node(1);
    pre_tree_edge(1, 1);
    for(int i = 1; i <= n; i++) Link(rt[cc[i]], 1, id, ys[i], w[i]);
    for(int i = 1; i <= m; i++) {
        scanf("%s", ss + 1);
        if(ss[1] == 'C') {
            int x, c; scanf("%d%d", &x, &c);
            del(rt[cc[x]], 1, id, ys[x]);
            if(ss[2] == 'C') {
                cc[x] = c;
                Link(rt[c], 1, id, ys[x], w[x]);
            }
            else {
                w[x] = c;
                Link(rt[cc[x]], 1, id, ys[x], c);
            }
        }
        else {
            int x, y; scanf("%d%d", &x, &y);
            if(ss[2] == 'S') printf("%d\n", solve1(x, y, cc[x]));
            else printf("%d\n", solve2(x, y, cc[x]));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值