[Sdoi 2014] bzoj3531 旅行 [树链剖分]

10 篇文章 0 订阅
3 篇文章 0 订阅

Description:
S S 国有N个城市,编号从 1 1 N。城市间用 N1 N − 1 条双向道路连接,满足
从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教, S S 国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
在S国的历史上常会发生以下几种事件:
CCxc: ” C C x c ” : 城市x的居民全体改信了 c c 教;
CWxw城市x的评级调整为 w; w ;
QSxy ” Q S x y ” : 一位旅行者从城市 x x 出发,到城市y,并记下了途中留宿过的城市的评级总和;
QMxy ” Q M x y ” : 一位旅行者从城市 x x 出发,到城市y,并记下了途中留宿过
的城市的评级最大值。
由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。


Solution:
树链剖分+动态开点线段树裸题。


#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 +5;
int n, q, dfs_clock, cnt;
int w[N], c[N], mir[N], sum[N * 30], mx[N * 30], lc[N * 30], rc[N * 30], root[N], sz[N], son[N], dfn[N], top[N], dep[N], fa[N];
vector<int> G[N];
void update(int l, int r, int &x, int p, int d) {
    if(!x) {
        x = ++cnt;
    }
    if(l == r) {
        mx[x] = sum[x] = d;
        return;
    }
    int mid = (l + r) >> 1;
    if(p <= mid) {
        update(l, mid, lc[x], p, d);
    } else {
        update(mid + 1, r, rc[x], p, d);
    }
    sum[x] = sum[lc[x]] + sum[rc[x]];
    mx[x] = max(mx[lc[x]], mx[rc[x]]);
}
int query_sum(int l, int r, int x, int a, int b) {
    if(l > b || r < a) {
        return 0;
    }
    if(l >= a && r <= b) {
        return sum[x];
    }
    int mid = (l + r) >> 1;
    return query_sum(l, mid, lc[x], a, b) + query_sum(mid + 1, r, rc[x], a, b);
}
int query_mx(int l, int r, int x, int a, int b) {
    if(l > b || r < a) {
        return 0;
    }
    if(l >= a && r <= b) {
        return mx[x];
    }
    int mid = (l + r) >> 1;
    return max(query_mx(l, mid, lc[x], a, b), query_mx(mid + 1, r, rc[x], a, b));
}
void dfs(int u, int last) {
    sz[u] = 1;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == last) {
            continue;
        }
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs(v, u);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) {
            son[u] = v;
        }
    }
}
void dfs(int u, int last, int acs) {
    dfn[u] = ++dfs_clock;
    mir[dfn[u]] = u;
    top[u] = acs;
    if(son[u]) {
        dfs(son[u], u, acs);
    }
    for(int i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if(v == last || v == son[u]) {
            continue;
        }
        dfs(v, u, v);
    }
}
int main() {
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; ++i) {
        scanf("%d%d", &w[i], &c[i]);
    }
    for(int i = 1; i < n; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    dfs(1, 0, 1);
    for(int i = 1; i <= n; ++i) {
        update(1, n, root[c[mir[i]]], i, w[mir[i]]);
    }
    while(q--) {
        char opt[4];
        int u, v;
        scanf("%s%d%d", opt, &u, &v);
        if(opt[0] == 'C' && opt[1] == 'C') {
            update(1, n, root[c[u]], dfn[u], 0);
            update(1, n, root[v], dfn[u], w[u]);
            c[u] = v;
        } else if(opt[0] == 'C' && opt[1] == 'W') {
            update(1, n, root[c[u]], dfn[u], v);
            w[u] = v;
        } else if(opt[0] == 'Q' && opt[1] == 'S') {
            int ret = 0, p = c[u];
            while(top[u] != top[v]) {
                if(dep[top[u]] < dep[top[v]]) {
                    swap(u, v);
                }
                ret += query_sum(1, n, root[p], dfn[top[u]], dfn[u]);
                u = fa[top[u]];
            }
            if(dfn[u] > dfn[v]) {
                swap(u, v);
            }
            ret += query_sum(1, n, root[p], dfn[u], dfn[v]);
            printf("%d\n", ret); 
        } else {
            int ret = 0, p = c[u];
            while(top[u] != top[v]) {
                if(dep[top[u]] < dep[top[v]]) {
                    swap(u, v);
                }
                ret = max(ret, query_mx(1, n, root[p], dfn[top[u]], dfn[u]));
                u = fa[top[u]];
            }
            if(dfn[u] > dfn[v]) {
                swap(u, v);
            }
            ret = max(ret, query_mx(1, n, root[p], dfn[u], dfn[v]));
            printf("%d\n", ret);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值