【bzoj3531】[Sdoi2014]旅行

题目链接

Description

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

Input

输入的第一行包含整数N,Q依次表示城市数和事件数。
接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的评级和信仰。
接下来N-1行每行两个整数x,y表示一条双向道路。
接下来Q行,每行一个操作,格式如上所述。

Output

对每个QS和QM事件,输出一行,表示旅行者记下的数字。

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

HINT

N,Q < =10^5 , C < =10^5

数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时

刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。

题解

树剖,然后对每一种宗教信仰开一棵线段树,里面仅有信仰该宗教的城市有对应的评价值。(开在一起以后就是一棵巨大无比的线段树了,很像主席树的感觉233)由于实在太大了所以动态开点。
剩下的操作就是对于对应信仰的线段树进行操作了。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 100000 + 10, M = 10000000 + 10;
int n, m, tot, sz, seg;
int w[N], c[N], rt[N];
int fa[N], dep[N], pos[N], son[N], bl[N], siz[N];
int to[N<<1], nxt[N<<1], hd[N];
int lk[M], rk[M], mx[M], sum[M];

void insert(int u, int v){
    to[++tot] = v; nxt[tot] = hd[u]; hd[u] = tot;
    to[++tot] = u; nxt[tot] = hd[v]; hd[v] = tot;
}

void dfs1(int x){
    siz[x] = 1;
    for(int i = hd[x]; i; i = nxt[i]){
        if(to[i] == fa[x]) continue;
        dep[to[i]] = dep[x] + 1;
        fa[to[i]] = x;
        dfs1(to[i]);
        if(siz[to[i]] > siz[son[x]]) son[x] = to[i];
        siz[x] += siz[to[i]];
    }
}
void dfs2(int x, int chain){
    pos[x] = ++sz;
    bl[x] = chain;
    if(!son[x]) return;
    dfs2(son[x], chain);
    for(int i = hd[x]; i; i = nxt[i])
        if(to[i] != fa[x] && to[i] != son[x])
         dfs2(to[i], to[i]);
}

void pup(int o){
    mx[o] = max(mx[lk[o]], mx[rk[o]]);
    sum[o] = sum[lk[o]] + sum[rk[o]];
}
void change(int &o, int l, int r, int x, int num){
    if(!o) o = ++seg;
    if(l == r){
        mx[o] = sum[o] = num;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) change(lk[o], l, mid, x, num);
    else change(rk[o], mid+1, r, x, num);
    pup(o);
}
int querymx(int o, int l, int r, int x, int y){
    if(!o) return 0;
    if(l == x && r == y) return mx[o];
    int mid = (l + r) >> 1;
    if(y <= mid) return querymx(lk[o], l, mid, x, y);
    else if(x > mid) return querymx(rk[o], mid+1, r, x, y);
    else return max(querymx(lk[o], l, mid, x, mid), querymx(rk[o], mid+1, r, mid+1, y));
}
int querysum(int o, int l, int r, int x, int y){
    if(!o) return 0;
    if(l == x && r == y) return sum[o];
    int mid = (l + r) >> 1;
    if(y <= mid) return querysum(lk[o], l, mid, x, y);
    else if(x > mid) return querysum(rk[o], mid+1, r, x, y);
    else return querysum(lk[o], l, mid, x, mid) + querysum(rk[o], mid+1, r, mid+1, y);
}

int solvemx(int c, int x, int y){
    int ans = 0;
    while(bl[x] != bl[y]){
        if(dep[bl[x]] < dep[bl[y]]) swap(x, y);
        ans = max(ans, querymx(rt[c], 1, n, pos[bl[x]], pos[x]));
        x = fa[bl[x]];
    }
    if(pos[x] > pos[y]) swap(x, y);
    ans = max(ans, querymx(rt[c], 1, n, pos[x], pos[y]));
    return ans;
}
int solvesum(int c, int x, int y){
    int ans = 0;
    while(bl[x] != bl[y]){
        if(dep[bl[x]] < dep[bl[y]]) swap(x, y);
        ans += querysum(rt[c], 1, n, pos[bl[x]], pos[x]);
        x = fa[bl[x]];
    }
    if(pos[x] > pos[y]) swap(x, y);
    ans += querysum(rt[c], 1, n, pos[x], pos[y]);
    return ans;
}

inline void in(int &x){
    x = 0; int f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    x *= f;
}
void init(){
    in(n); in(m);
    for(int i = 1; i <= n; i++) in(w[i]), in(c[i]);
    for(int i = 1; i < n; i++){
        int x, y; in(x); in(y);
        insert(x, y);
    }
    dfs1(1); dfs2(1, 1);
}

void work(){
    for(int i = 1; i <= n; i++)
        change(rt[c[i]], 1, n, pos[i], w[i]);
    char s[10];
    int x, y;
    for(int i = 1; i <= m; i++){
        scanf("%s", s);
        in(x); in(y);
        if(s[0] == 'C'){
            if(s[1] == 'C'){
                change(rt[c[x]], 1, n, pos[x], 0);
                c[x] = y;
                change(rt[c[x]], 1, n, pos[x], w[x]);
            }
            else { change(rt[c[x]], 1, n, pos[x], y); w[x] = y; }
        }
        else{
            if(s[1] == 'S')
                printf("%d\n", solvesum(c[x], x, y));
            else printf("%d\n", solvemx(c[x], x, y));
        }
    }
}

int main(){
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值