BZOJ - 1036 树的统计Count(线段树树链剖分裸题)

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 19039   Solved: 7761
[ Submit][ Status][ Discuss]

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

  对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

树链剖分裸题

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

const int N = 3e4 +10;
int n, m, tot, tot1;
int num[N], tree[N], pre[N], head[N], son[N], fa[N], data[N], top[N], deep[N];
struct xx{
    int l, r, sum, maxi;
} T[N<<2];
struct xxx{
    int to, nex;
} a[N<<1];

void Add(int u, int v){
    a[++tot].to = v, a[tot].nex = head[u], head[u] = tot;
}

void dfs1(int k, int f, int d){
    deep[k] = d, fa[k] = f, num[k] = 1;
    for(int i = head[k]; i != -1; i = a[i].nex){
        int to = a[i].to;
        if(to == f) continue;
        dfs1(to, k, d+1);
        num[k] += num[to];
        if(!son[k] || num[to] > num[son[k]]) son[k] = to;
    }
}

void dfs2(int k, int Number){
    top[k] = Number, tree[k] = ++tot1;
    pre[tree[k]] = k;
    if(!son[k]) return;
    dfs2(son[k], Number);
    for(int i = head[k]; i != -1; i = a[i].nex){
        int to = a[i].to;
        if(to != son[k] && to != fa[k]) dfs2(to, to);
    }
}

void Build(int l, int r, int k){
    T[k].l = l, T[k].r = r;
    if(l == r){
        T[k].sum = T[k].maxi = data[pre[l]];
        return;
    }
    int mid = (l+r)>>1;
    Build(l, mid, k<<1);
    Build(mid+1, r, k<<1|1);
    T[k].maxi = max(T[k<<1].maxi, T[k<<1|1].maxi);
    T[k].sum = T[k<<1].sum+T[k<<1|1].sum;
}

void Update(int x, int v, int k){
    if(T[k].l == T[k].r && T[k].l == x){
        T[k].sum = T[k].maxi = v;
        return;
    }
    int mid = (T[k].l+T[k].r)>>1;
    if(x <= mid) Update(x, v, k<<1);
    else Update(x, v, k<<1|1);
    T[k].maxi = max(T[k<<1].maxi, T[k<<1|1].maxi);
    T[k].sum = T[k<<1].sum+T[k<<1|1].sum;
}

int Max;
void Query_max(int l, int r, int k){
    if(l <= T[k].l && r >= T[k].r){
        Max = max(Max, T[k].maxi);
        return;
    }
    if(T[k].l == T[k].r) return;
    int mid = (T[k].l+T[k].r)>>1;
    if(r <= mid) Query_max(l, r, k<<1);
    else if(l > mid) Query_max(l, r, k<<1|1);
    else{
        Query_max(l, mid, k<<1);
        Query_max(mid+1, r, k<<1|1);
    }
}

int Sum;
void Query_sum(int l, int r, int k){
    if(l <= T[k].l && r >= T[k].r){
        Sum += T[k].sum;
        return;
    }
    if(T[k].l == T[k].r) return;
    int mid = (T[k].l+T[k].r)>>1;
    if(r <= mid) Query_sum(l, r, k<<1);
    else if(l > mid) Query_sum(l, r, k<<1|1);
    else{
        Query_sum(l, mid, k<<1);
        Query_sum(mid+1, r, k<<1|1);
    }
}

int Get_max(int x, int y){
    int f1 = top[x], f2 = top[y];
    Max = -0x3f3f3f3f;
    while(f1 != f2){
        if(deep[f1] < deep[f2]){
            swap(f1, f2);
            swap(x, y);
        }
        Query_max(tree[f1], tree[x], 1);
        x = fa[f1], f1 = top[x];
    }
    deep[x] > deep[y] ? Query_max(tree[y], tree[x], 1) : Query_max(tree[x], tree[y], 1);
    return Max;
}

int Get_sum(int x, int y){
    int f1 = top[x], f2 = top[y];
    Sum = 0;
    while(f1 != f2){
        if(deep[f1] < deep[f2]){
            swap(f1, f2);
            swap(x, y);
        }
        Query_sum(tree[f1], tree[x], 1);
        x = fa[f1], f1 = top[x];
    }
    deep[x] > deep[y] ? Query_sum(tree[y], tree[x], 1) : Query_sum(tree[x], tree[y], 1);
    return Sum;
}

int main(){
    while(scanf("%d", &n) == 1){
        tot = tot1 = 0;
        int u, v;
        memset(head, -1, sizeof head);
        for(int i = 1; i < n; i++){
            scanf("%d%d", &u, &v);
            Add(u, v); Add(v, u);
        }
        for(int i = 1; i <= n; i++){
            scanf("%d", &data[i]);
        }
        dfs1(1, 0, 1); dfs2(1, 1);
        Build(1, n, 1);
        scanf("%d", &m);
        char op[10];
        while(m--){
            scanf("%s%d%d", op, &u, &v);
            if(op[1] == 'H') Update(tree[u], v, 1);
            else if(op[1] == 'M') printf("%d\n", Get_max(u, v));
            else printf("%d\n", Get_sum(u, v));
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值