luoguP2590 【[ZJOI2008]树的统计】

树剖模板题啊!

这道题的话,最通(jian)俗(dan)易(cu)懂(bao)的解法应该就是树剖了。
加上线段树维护树上路径的最大权值(\(Max\))和路径和(\(sum\))。
至于\(LCT\)这种高级操作对于我这种新手还是比较困难的\(qwq\)
以下是参考代码:(有什么问题欢迎指教!)

#include<cstdio>
const int MAXN=30010;
inline int max(int a,int b){return a>b?a:b;}
inline int read(){
    int s=0;bool f=false;char c=getchar();
    while(c<'0'||c>'9')f|=(c=='-'),c=getchar();
    while(c>='0'&&c<='9')s=(s<<3)+(s<<1)+(c^48),c=getchar();
    return (f)?(-s):(s);
}
int n,m,summ,maxx;
int seg[MAXN],rev[MAXN],top[MAXN];
int dep[MAXN],siz[MAXN],son[MAXN],father[MAXN];
int tot,ver[MAXN<<1],fir[MAXN<<1],nxt[MAXN<<1];
inline void Add_edge(int u,int v){
    nxt[++tot]=fir[u],fir[u]=tot,ver[tot]=v;
    nxt[++tot]=fir[v],fir[v]=tot,ver[tot]=u;
}
int val[MAXN],sum[MAXN<<2],Max[MAXN<<2];
inline int lc(int rt){return rt<<1;}
inline int rc(int rt){return rt<<1|1;}
inline void push_sum(int rt){
    sum[rt]=sum[lc(rt)]+sum[rc(rt)];
}
inline void push_max(int rt){
    Max[rt]=max(Max[lc(rt)],Max[rc(rt)]);
}
inline void build(int rt,int l,int r){
    if(l==r)
        sum[rt]=Max[rt]=val[rev[l]];
    else{
        int mid=(l+r)>>1;
        build(lc(rt),l,mid);
        build(rc(rt),mid+1,r);
        push_sum(rt),push_max(rt);
    }
}
inline void update(int rt,int l,int r,int id,int v){
    if(id<l||id>r) return;
    if(l==r&&l==id)
        sum[rt]=Max[rt]=v;
    else{
        int mid=(l+r)>>1;
        update(lc(rt),l,mid,id,v);
        update(rc(rt),mid+1,r,id,v);
        push_sum(rt),push_max(rt);
    }
}
inline void query(int rt,int l,int r,int x,int y){
    if(r<x||l>y) return;
    if(x<=l&&r<=y)
        summ+=sum[rt],maxx=max(maxx,Max[rt]);
    else{
        int mid=(l+r)>>1;
        query(lc(rt),l,mid,x,y);
        query(rc(rt),mid+1,r,x,y);
    }
}
inline void dfs1(int u,int f){
    dep[u]=dep[f]+1,siz[u]=1,father[u]=f;
    for(int v,i=fir[u];i;i=nxt[i])
        if((v=ver[i])!=f){
            dfs1(v,u),siz[u]+=siz[v];
            if(siz[son[u]]<siz[v])son[u]=v;
        }
}
inline void dfs2(int u,int topf){
    top[rev[seg[u]=++seg[0]]=u]=topf;
    if(!son[u])return;else dfs2(son[u],topf);
    for(int v,i=fir[u];i;i=nxt[i])
        if(!top[v=ver[i]])dfs2(v,v);
}
inline void ask(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]>=dep[fy]){
            query(1,1,n,seg[fx],seg[x]);
            x=father[fx],fx=top[x];
        }
        else{
            query(1,1,n,seg[fy],seg[y]);
            y=father[fy],fy=top[y];
        }
    }
    if(dep[x]<=dep[y])
        query(1,1,n,seg[x],seg[y]);
    else
        query(1,1,n,seg[y],seg[x]);
}
int main(){
    n=read();
    for(int i=1;i<n;++i)
        Add_edge(read(),read());
    for(int i=1;i<=n;++i)
        val[i]=read();
    dfs1(1,0),dfs2(1,1),build(1,1,n);
    m=read();
    char opt[10];
    while(m--){
        scanf("%s",opt);
        int x=read(),y=read();
        if(opt[0]=='C')
            update(1,1,n,seg[x],y);
        else{
            summ=0;
            maxx=-10000000;
            ask(x,y);
            if(opt[1]=='M')
                printf("%d\n",maxx);
            else
                printf("%d\n",summ);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/zsbzsb/p/11000516.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值