bzoj1036树的统计 树链剖分+线段树

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 18203   Solved: 7415
[ 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

HINT

Source

裸的树剖+线段树

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 33001
#define maxm 66001
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
 
int readchar()
{
    char ch = getchar();
    while(ch != 'C' && ch != 'Q') ch = getchar();
    if(ch == 'C') return 3;
    else {
        ch = getchar();
        if(ch == 'S') return 1;
        else return 2;
    }
}
 
int pre[maxn], top;
struct edge {
    int to, next;
    void add(int a, int b) {
        to = b;
        next = pre[a];
        pre[a] = top++;
    }
}e[maxm];
int n, m, tot, w[maxn], f[maxn], d[maxn], pos[maxn], deep[maxn], son[maxn];
 
struct tree {
    int l, r, sum, mx;
    tree() : l(0), r(0), sum(0), mx(-inf) {} 
}t[maxn * 4];
 
void adds(int u, int v)
{
    e[top].add(u, v);
    e[top].add(v, u);
}
 
void dfs1(int u, int fa)
{
    son[u] = 1; f[u] = fa; deep[u] = deep[fa] + 1;
    for(int i = pre[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa) continue;
        dfs1(v, u);
        son[u] += son[v];
    }
}
 
void dfs2(int u, int chain)
{
    pos[u] = ++tot; d[u] = chain; int k = 0;
    for(int i = pre[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v != f[u] && son[v] > son[k]) k = v;
    }
    if(!k) return;
    dfs2(k, chain);
    for(int i = pre[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == f[u] || v == k) continue;
        dfs2(v, v);
    }
}
 
void build_tree(int p, int L, int R)
{
    t[p].l = L; t[p].r = R; t[p].sum = 0;
    if(L == R) return;
    int mid = L + R >> 1;
    build_tree(p << 1, L, mid);
    build_tree(p << 1 | 1, mid + 1, R);
}
 
void change(int p, int pos, int value) 
{
    int l = t[p].l, r = t[p].r;
    if(l == r) {
        t[p].mx = t[p].sum = value;
        return;
    }
    int mid = l + r >> 1;
    if(pos <= mid) change(p << 1, pos, value);
    else change(p << 1 | 1, pos, value);
    t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;
    t[p].mx = max(t[p << 1].mx, t[p << 1 | 1].mx);
}
 
int query_sum(int p, int L, int R)
{
    int l = t[p].l, r = t[p].r;
    if(l == L && r == R) return t[p].sum;
    int mid = l + r >> 1;
    int sum = 0;
    if(L <= mid) sum += query_sum(p << 1, L, min(mid, R));
    if(R > mid) sum += query_sum(p << 1 | 1, max(mid + 1, L), R);
    return sum;
}
 
int query_max(int p, int L, int R)
{
    int l = t[p].l, r = t[p].r;
    if(l == L && r == R) return t[p].mx;
    int mid = l + r >> 1;
    int mx = -inf;
    if(L <= mid) mx = max(mx, query_max(p << 1, L, min(mid, R)));
    if(R > mid) mx = max(mx, query_max(p << 1 | 1, max(mid + 1, L), R));
    return mx;
}
 
int ask_sum(int u, int v)
{
    int ans = 0;
    while(d[u] != d[v])
    {
        if(deep[d[u]] < deep[d[v]]) swap(u, v);
        ans += query_sum(1, pos[d[u]], pos[u]);
        u = f[d[u]];
    }
    if(pos[u] < pos[v]) swap(u, v);
    ans += query_sum(1, pos[v], pos[u]);
    return ans;
}
 
int ask_max(int u, int v)
{
    int ans = -inf;
    while(d[u] != d[v])
    {
        if(deep[d[u]] < deep[d[v]]) swap(u, v);
        ans = max(ans, query_max(1, pos[d[u]], pos[u]));
        u = f[d[u]];
    }
    if(pos[u] < pos[v]) swap(u, v);
    ans = max(ans, query_max(1, pos[v], pos[u]));
    return ans;
}
 
void init()
{
    n = read();
    memset(pre, -1, sizeof(pre)); tot = top = 0;
    for(int i = 1;i < n; ++i) adds(read(), read());
    for(int i = 1;i <= n; ++i) w[i] = read();
    dfs1(1, 0);
    dfs2(1, 1);
    build_tree(1, 1, n);
    for(int i = 1;i <= n; ++i) change(1, pos[i], w[i]);
}
 
void solve()
{
    m = read();
    for(int i = 1;i <= m; ++i)
    {
        int p = readchar(); int x = read(), y = read();
        if(p == 1) 
            printf("%d\n", ask_sum(x, y));
        else if(p == 2) printf("%d\n", ask_max(x, y));
        else {
            w[x] = y;
            change(1, pos[x], y);
        }
    }
}
 
int main()
{
    init();
    solve();
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值