[ZJOI2008]树的统计——[树链剖分]

【题目描述】

一棵树上有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”的形式给出。

【输出格式】
对于每个“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

【题意分析】
ZJOI真是暴力!!

又见暴力树剖模板,我们需要两个线段树:一个记录区间最大值,一个保留区间和。

因为是单点修改所以连lazytag都不需要啦!

最近发现我的快读老是爆炸,索性都不打快读了。

这道题给你的权值会是负数,蒟蒻写querymax函数时一开始return 0,读题好久才在题面底部发现权值可以是负数,于是改成return -INF。

就过啦!!!!

树剖程序长,做的是细心。

Code:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAX 100000
#define INF 1 << 29
using namespace std;

struct Front_Link_Star{
    int next,to;
}edge[MAX];

int sum[MAX << 2],maxx[MAX << 2],id[MAX],top[MAX],father[MAX],depth[MAX];
int size[MAX],rk[MAX],a[MAX],head[MAX],son[MAX],cnt,dfn,n,m;
//sum[]记录区间和,maxx[]记录区间最大值

inline void Add_Edge(int u,int v){
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}   //链式前向星

inline void push_up(int now){
    sum[now]=sum[now << 1]+sum[now << 1|1];
    maxx[now]=max(maxx[now << 1],maxx[now << 1|1]);
}   //上传

inline void build(int now,int tl,int tr){
    if (tl==tr){
        sum[now]=maxx[now]=rk[tl];
        return;
    }
    int mid=(tl+tr) >> 1;
    build(now << 1,tl,mid);
    build(now << 1|1,mid+1,tr);
    push_up(now);
}   //建树

inline void update(int now,int tl,int tr,int left,int right,int change){
    if (right<tl||tr<left)return;
    if (left<=tl&&tr<=right){
        sum[now]=maxx[now]=change;
        return;
    }
    int mid=(tl+tr) >> 1;
    update(now << 1,tl,mid,left,right,change);
    update(now << 1|1,mid+1,tr,left,right,change);
    push_up(now);
}    //单点修改

inline int querymax(int now,int tl,int tr,int left,int right){
    if (right<tl||tr<left)return -INF;
    //就是这里,我原来打了return 0,因为0会比负数大,所以要打-INF
    if (left<=tl&&tr<=right)return maxx[now];
    int mid=(tl+tr) >> 1;
    int ans=-INF;
    ans=max(ans,querymax(now << 1,tl,mid,left,right));
    ans=max(ans,querymax(now << 1|1,mid+1,tr,left,right));
    return ans;
}      //查询区间最大值

inline int querysum(int now,int tl,int tr,int left,int right){
    if (right<tl||tr<left)return 0;
    if (left<=tl&&tr<=right)return sum[now];
    int mid=(tl+tr) >> 1;
    int ans=0;
    ans+=querysum(now << 1,tl,mid,left,right);
    ans+=querysum(now << 1|1,mid+1,tr,left,right);
    return ans;
}   //查询区间和

inline int Query_Max(int x,int y){
    int ans=-INF;
    while (top[x]!=top[y]){
        if (depth[top[x]]<depth[top[y]])swap(x,y);
        ans=max(ans,querymax(1,1,n,id[top[x]],id[x]));
        x=father[top[x]];
    }
    if (depth[x]>depth[y])swap(x,y);
    ans=max(ans,querymax(1,1,n,id[x],id[y]));
    return ans;
}    //查询路径最大值

inline int Query_Sum(int x,int y){
    int ans=0;
    while (top[x]!=top[y]){
        if (depth[top[x]]<depth[top[y]])swap(x,y);
        ans+=querysum(1,1,n,id[top[x]],id[x]);
        x=father[top[x]];
    }
    if (depth[x]>depth[y])swap(x,y);
    ans+=querysum(1,1,n,id[x],id[y]);
    return ans;
}   //查询路径和

inline void DFS1(int now,int fa,int d){
    father[now]=fa;
    depth[now]=d;
    size[now]=1;
    int maxson=-1;
    for (register int i=head[now];i;i=edge[i].next){
        int v=edge[i].to;
        if (v==fa)continue;
        DFS1(v,now,d+1);
        size[now]+=size[v];
        if (maxson<size[v]){
            maxson=size[v];
            son[now]=v;
        }
    }
}

inline void DFS2(int now,int top_heavy){
    top[now]=top_heavy;
    id[now]=++dfn;
    rk[dfn]=a[now];
    if (!son[now])return;
    DFS2(son[now],top_heavy);
    for (register int i=head[now];i;i=edge[i].next){
        int v=edge[i].to;
        if (v!=father[now]&&v!=son[now])DFS2(v,v);
    }
}    //树剖

int main(){
    scanf("%d",&n);
    for (register int i=1;i<=n-1;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        Add_Edge(x,y);
        Add_Edge(y,x);
    }
    for (register int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    DFS1(1,0,1);
    DFS2(1,1);
    build(1,1,n);
    scanf("%d",&m);
    while (m--){
        int x,y;
        char s[10];
        scanf("%s%d%d",&s,&x,&y);
        if (s[1]=='M')printf("%d\n",Query_Max(x,y));
        if (s[1]=='S')printf("%d\n",Query_Sum(x,y));
        if (s[1]=='H')update(1,1,n,id[x],id[x],y);
        //这里是单点修改
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mega_Aspirin/article/details/81480130