BZOJ 1984 月下“毛景树”

题目描述
毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园。 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里。爬啊爬~爬啊爬毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~ “毛景树”上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的。但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数:  Change k w:将第k条树枝上毛毛果的个数改变为w个。

 Cover u v w:将节点u与节点v之间的树枝上毛毛果的个数都改变为w个。

 Add u v w:将节点u与节点v之间的树枝上毛毛果的个数都增加w个。 由于毛毛虫很贪,于是他会有如下询问:

 Max u v:询问节点u与节点v之间树枝上毛毛果个数最多有多少个。

输入
第一行一个正整数N。 接下来N-1行,每行三个正整数Ui,Vi和Wi,第i+1行描述第i条树枝。表示第i条树枝连接节点Ui和节点Vi,树枝上有Wi个毛毛果。 接下来是操作和询问,以“Stop”结束。

输出
对于毛毛虫的每个询问操作,输出一个答案。

样例输入
4
1 2 8
1 3 7
3 4 9
Max 2 4
Cover 2 4 5
Add 1 4 10
Change 1 16
Max 2 4
Stop
样例输出
9
16

提示
n<=100000

题解:这是一道树剖维护边权裸题。和一般的树剖差不多,我们只要将边权放在深度较大的点上,再跑一般的树剖即可。但是这里有一个很重要的细节:对于查询(或修改)树上两点之间的路径上的边权时,不能考虑到他们的LCA。因为它们的LCA存的是他到他的父亲的边的权值,而这一条边实际上并不在这两点的路径上。(详细请参考代码注释)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 100005
#define inf 1000000009
using namespace std;
int hd[N],nx[N<<1],to[N<<1],w[N<<1],cnt,u,v,c;
int n,val[N],edge[N],tr[N<<2],isp[N<<2],laz[N<<2];
int fa[N],dep[N],siz[N],son[N],did[N],tot,tid[N],top[N];
inline void add(){
    to[++cnt]=v;w[cnt]=c;nx[cnt]=hd[u];hd[u]=cnt;
    to[++cnt]=u;w[cnt]=c;nx[cnt]=hd[v];hd[v]=cnt;
}
inline void dfs1(int x,int f,int d){
    fa[x]=f;
    dep[x]=d;
    siz[x]=1;
    for(int i=hd[x];i;i=nx[i]){
        int t=to[i];
        if(t!=fa[x]){
            dfs1(t,x,d+1);
            val[t]=w[i];
            edge[i+1>>1]=t;
            siz[x]+=siz[t];
            if(son[x]<0||siz[t]>siz[son[x]]) son[x]=t;
        }
    }
}
inline void dfs2(int x,int s){
    top[x]=s;
    did[x]=++tot;
    tid[tot]=x;
    if(son[x]<0) return;
    dfs2(son[x],s);
    for(int i=hd[x];i;i=nx[i]){
        int t=to[i];
        if(t!=son[x]&&t!=fa[x]) dfs2(t,t);
    }
}
inline void pushdown(int id){
    if(isp[id]){
        isp[id<<1]=isp[id<<1|1]=isp[id];
        tr[id<<1]=tr[id<<1|1]=isp[id];
        laz[id<<1]=laz[id<<1|1]=isp[id]=0;
    }
    if(laz[id]){
        laz[id<<1]+=laz[id];
        laz[id<<1|1]+=laz[id];
        tr[id<<1]+=laz[id];
        tr[id<<1|1]+=laz[id];
        laz[id]=0;
    }
}
inline void update(int id){
    tr[id]=max(tr[id<<1],tr[id<<1|1]);
}
inline void build(int id,int l,int r){
    if(l==r){
        tr[id]=val[tid[l]];
        return;
    }
    int mid=l+r>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    update(id);
}
inline void interpi(int id,int l,int r,int ll,int rr,int v){
    if(ll>rr) return;
    if(ll<=l&&rr>=r){
        tr[id]=v;
        isp[id]=v;
        laz[id]=0;
        return;
    }
    pushdown(id);
    int mid=l+r>>1;
    if(ll<=mid) interpi(id<<1,l,mid,ll,rr,v);
    if(rr>mid) interpi(id<<1|1,mid+1,r,ll,rr,v);
    update(id);
}
inline void interadd(int id,int l,int r,int ll,int rr,int v){
    if(ll>rr) return;
    if(ll<=l&&rr>=r){
        tr[id]+=v;
        laz[id]+=v;
        return;
    }
    pushdown(id);
    int mid=l+r>>1;
    if(ll<=mid) interadd(id<<1,l,mid,ll,rr,v);
    if(rr>mid) interadd(id<<1|1,mid+1,r,ll,rr,v);
    update(id);
}
inline int query(int id,int l,int r,int ll,int rr){
    if(ll>rr) return -inf;
    if(ll<=l&&rr>=r) return tr[id];
    pushdown(id);
    int mid=l+r>>1,ans=-inf;
    if(ll<=mid) ans=max(ans,query(id<<1,l,mid,ll,rr));
    if(rr>mid) ans=max(ans,query(id<<1|1,mid+1,r,ll,rr));
    return ans;
}
inline void ipi(int x,int y,int v){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        interpi(1,1,n,did[top[x]],did[x],v);
        x=fa[top[x]];
    }
    if(did[x]>did[y]) swap(x,y);
    interpi(1,1,n,did[x]+1,did[y],v);
    //这里的did[x]+1就是为了不算上LCA,下面同理
}
inline void iadd(int x,int y,int v){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        interadd(1,1,n,did[top[x]],did[x],v);
        x=fa[top[x]];
    }
    if(did[x]>did[y]) swap(x,y);
    interadd(1,1,n,did[x]+1,did[y],v);
}
inline int q(int x,int y){
    int ans=-inf;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ans=max(ans,query(1,1,n,did[top[x]],did[x]));
        x=fa[top[x]];
    }
    if(did[x]>did[y]) swap(x,y);
    ans=max(ans,query(1,1,n,did[x]+1,did[y]));
    return ans;
}
int main(){
    memset(son,-1,sizeof son);
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&u,&v,&c);
        add();
    }
    dfs1(1,0,1);
    dfs2(1,1);
    build(1,1,n);
    while(1){
        char ch[10];
        int u,v,w;
        scanf(" %s",&ch[0]);
        if(ch[0]=='S') break;
        scanf("%d%d",&u,&v);
        if(ch[1]=='h')
            interpi(1,1,n,did[edge[u]],did[edge[u]],v);
        else if(ch[1]=='o'){
            scanf("%d",&w);
            ipi(u,v,w);
        }
        else if(ch[1]=='d'){
            scanf("%d",&w);
            iadd(u,v,w);
        }
        else printf("%d\n",q(u,v));
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值