【bzoj1036】[ZJOI2008]树的统计Count 树链剖分+线段树

时间:2023-01-06 09:52:44

题目描述

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作:

I. CHANGE u t : 把结点u的权值改为t  

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值  

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

输入

输入的第一行为一个整数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之间。

输出

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

样例输入

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

样例输出

4
1
2
2
10
6
5
6
5
16


题解

树链剖分模板题,第一次码。。。

树链剖分就是把一棵树拆为一些链来处理,并用数据结构来维护。

通常的剖分方法是按照轻重链剖分,每次选取节点最多的子树继承重链,其余视作轻链,重新处理。

通常用线段树维护权值。

查询时,如果x和y在同一条链上,那么直接查询,否则一边将x和y往同一条链上靠,一边记录路径上的权值,然后查询。

代码应该不难理解。

#include <cstdio>
#include <algorithm>
using namespace std;
#define inf 0x3fffffff
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
int val[30005] , deep[30005] , si[30005] , fa[30005] , bl[30005] , pos[30005] , tot;
int head[30005] , to[60005] , next[60005] , cnt;
int maxn[120005] , sum[120005] , n;
char str[10];
void add(int x , int y)
{
    to[++cnt] =y;
    next[cnt] = head[x];
    head[x] = cnt;
}
void dfs1(int x)
{
    si[x] = 1;
    int i , y;
    for(i = head[x] ; i ; i = next[i])
    {
        y = to[i];
        if(y != fa[x])
        {
            deep[y] = deep[x] + 1;
            fa[y] = x;
            dfs1(y);
            si[x] += si[y];
        }
    }
}
void dfs2(int x , int c)
{
    int k = 0 , i , y;
    pos[x] = ++tot;
    bl[x] = c;
    for(i = head[x] ; i ; i = next[i])
    {
        y = to[i];
        if(deep[y] > deep[x] && si[y] > si[k])
            k = y;
    }
    if(k != 0)
    {
        dfs2(k , c);
        for(i = head[x] ; i ; i = next[i])
        {
            y = to[i];
            if(deep[y] > deep[x] && y != k)
                dfs2(y , y);
        }
    }
}
void pushup(int x)
{
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
    maxn[x] = max(maxn[x << 1] , maxn[x << 1 | 1]);
}
void update(int p , int c , int l , int r , int x)
{
    if(l == r)
    {
        sum[x] = maxn[x] = c;
        return;
    }
    int mid = (l + r) >> 1;
    if(p <= mid)
        update(p , c , lson);
    else
        update(p , c , rson);
    pushup(x);
}
int querysum(int b , int e , int l , int r , int x)
{
    if(b <= l && r <= e)
        return sum[x];
    int mid = (l + r) >> 1 , ans = 0;
    if(b <= mid)
        ans += querysum(b , e , lson);
    if(e > mid)
        ans += querysum(b , e , rson);
    return ans;
}
int querymaxn(int b , int e , int l , int r , int x)
{
    if(b <= l && r <= e)
        return maxn[x];
    int mid = (l + r) >> 1 , ans = -inf;
    if(b <= mid)
        ans = max(ans , querymaxn(b , e , lson));
    if(e > mid)
        ans = max(ans , querymaxn(b , e , rson));
    return ans;
}
int solvesum(int x , int y)
{
    int ans = 0;
    while(bl[x] != bl[y])
    {
        if(deep[bl[x]] < deep[bl[y]])
            swap(x , y);
        ans += querysum(pos[bl[x]] , pos[x] , 1 , n , 1);
        x = fa[bl[x]];
    }
    if(pos[x] > pos[y])
        swap(x , y);
    ans += querysum(pos[x] , pos[y] , 1 , n , 1);
    return ans;
}
int solvemaxn(int x , int y)
{
    int ans = -inf;
    while(bl[x] != bl[y])
    {
        if(deep[bl[x]] < deep[bl[y]])
            swap(x , y);
        ans = max(ans , querymaxn(pos[bl[x]] , pos[x] , 1 , n , 1));
        x = fa[bl[x]];
    }
    if(pos[x] > pos[y])
        swap(x , y);
    ans = max(ans , querymaxn(pos[x] , pos[y] , 1 , n , 1));
    return ans;
}
int main()
{
    int i , x , y , q;
    scanf("%d" , &n);
    for(i = 1 ; i < n ; i ++ )
    {
        scanf("%d%d" , &x , &y);
        add(x , y);
        add(y , x);
    }
    for(i = 1 ; i <= n ; i ++ )
        scanf("%d" , &val[i]);
    dfs1(1);
    dfs2(1 , 1);
    for(i = 1 ; i <= n ; i ++ )
        update(pos[i] , val[i] , 1 , n , 1);
    scanf("%d" , &q);
    while(q -- )
    {
        scanf("%s%d%d" , str , &x , &y);
        switch(str[1])
        {
            case 'H': val[x] = y; update(pos[x] , y , 1 , n , 1); break;
            case 'S': printf("%d\n" , solvesum(x , y)); break;
            default: printf("%d\n" , solvemaxn(x , y));
        }
    }
    return 0;
}