BZOJ1036: [ZJOI2008]树的统计Count

1036: [ZJOI2008]树的统计Count

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

    [这里写链接内容](http://hzwer.com/2543.html)
    这题可以练一下树链剖分。
    由题意得,是求树上两点的lca,lca可以用倍增表或者树剖写。
    但因为要支持修改,而倍增表不支持修改,所以得用树链剖分。
    修改和查询可以用线段树维护即可
    本人蒟蒻,不会splay,所以这里不讲
    用线段树维护最大值和总和,求出lca,时间复杂度为(N log^2)
    注意这道题有负数,用了读入优化或输出优化的同学注意。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define N 30005
using namespace std;
int n,cnt,sz;
int v[N],size[N],deep[N],son[N],head[N],fa[N];
int pos[N],bl[N];
struct tre{
    int l,r,mx,sum;
}t[N*4];
struct edge{
    int next,to;
}e[N*2];

inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-48;
    return x*f;
}

inline void write(int x){
    int a[30],f=1;a[0]=0;
    if(x<0) f=-1,x=-x;
    for(;!a[0]||x;x/=10) a[++a[0]]=x%10;
    if(f==-1) putchar('-');
    for(int i=a[0];i>0;i--) putchar(a[i]+48);
}

inline void writeln(){
    putchar('\n');
}

inline void insert(int u,int v){
    e[++cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}

inline void dfs1(int x){
    size[x]=1;
    for(int i=head[x];i;i=e[i].next){
        if(e[i].to==fa[x]) continue;
        fa[e[i].to]=x,deep[e[i].to]=deep[x]+1;
        dfs1(e[i].to);
        size[x]+=size[e[i].to];
    }
}

inline void dfs2(int x,int tp){
    int k=0;
    pos[x]=++sz;bl[x]=tp;
    for(int i=head[x];i;i=e[i].next)
        if(deep[e[i].to]>deep[x]&&size[e[i].to]>size[k]) k=e[i].to;
    if(!k) return;
    dfs2(k,tp);
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=k&&deep[e[i].to]>deep[x]) dfs2(e[i].to,e[i].to);
}

inline void build(int node,int l,int r){
    t[node].l=l,t[node].r=r;
    if(l==r) return;
    int mid=(l+r)/2;
    build(node*2,l,mid);
    build(node*2+1,mid+1,r);
}

inline void update(int node,int v,int val){
    int l=t[node].l,r=t[node].r;
    if(l==r){
        t[node].sum=t[node].mx=val;
        return;
    }
    int mid=(l+r)/2;
    if(v<=mid) update(node*2,v,val);
    else update(node*2+1,v,val);
    t[node].sum=t[node*2].sum+t[node*2+1].sum;
    t[node].mx=max(t[node*2].mx,t[node*2+1].mx);
}

inline int query_smax(int node,int l,int r){
    if(t[node].l>=l&&t[node].r<=r) return t[node].mx;
    int mid=(t[node].l+t[node].r)/2;
    if(r<=mid) return query_smax(node*2,l,r);
    else if(l>mid) return query_smax(node*2+1,l,r);
    else return max(query_smax(node*2,l,mid),query_smax(node*2+1,mid+1,r));
}

inline int query_ssum(int node,int l,int r){
    if(t[node].l>=l&&t[node].r<=r) return t[node].sum;
    int mid=(t[node].l+t[node].r)/2;
    if(r<=mid) return query_ssum(node*2,l,r);
    else if(l>mid) return query_ssum(node*2+1,l,r);
    else return query_ssum(node*2,l,mid)+query_ssum(node*2+1,mid+1,r);
}

inline int smax(int x,int y){//lca 求最大值
    int mx=-1e9;
    while(bl[x]!=bl[y]){
        if(deep[bl[x]]<deep[bl[y]]) swap(x,y);
        mx=max(mx,query_smax(1,pos[bl[x]],pos[x]));
        x=fa[bl[x]];
    }
    if(pos[x]>pos[y]) swap(x,y);
    mx=max(mx,query_smax(1,pos[x],pos[y]));
    return mx;
}

inline int ssum(int x,int y){//LCA 求和
    int sum=0;
    while(bl[x]!=bl[y]){
        if(deep[bl[x]]<deep[bl[y]]) swap(x,y);
        sum+=query_ssum(1,pos[bl[x]],pos[x]);
        x=fa[bl[x]];
    }
    if(pos[x]>pos[y]) swap(x,y);
    sum+=query_ssum(1,pos[x],pos[y]);
    return sum;
}

int main(){
    n=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        insert(u,v);
        insert(v,u);
    }
    for(int i=1;i<=n;i++) v[i]=read();
    dfs1(1),dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=n;i++) update(1,pos[i],v[i]);
    int m=read();
    while(m--){
        char ch[10];
        scanf("%s",ch);
        int x=read(),y=read();
        if(ch[1]=='H') v[x]=y,update(1,pos[x],y);
        else if(ch[1]=='M') write(smax(x,y)),writeln();
        else write(ssum(x,y)),writeln();
    }
    return 0;
}

orz hzwer

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值