C A National Pandemic(树上点扩减值,树剖)

题:https://ac.nowcoder.com/acm/contest/5672/C

题意:给定树,m个操作:(1)在x点处增加w,树上每个点y的值+=w-dis(x,y)。

            (2)将x点处值和0取min

            (3)查询x点的值

分析:将每个点x 的值展开=w-dep(x)-dep(y)+2*dep(lca)。我分成(w-dep(x)),-(dep(y)),(2*dep(lca))来算

   对于前俩部分我们很容易在边操作时边统计出来,对于第三部分我们用树剖维护,若在x进行1操作,那么就在x与根节点路径上的所有点都加2,查询就查询出的x'到根节点路径和就是第三部分的值(因为若要查询当前的x',x'与之前x的lca的dep*2就为lca到根节点路径长*2)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define lson root<<1,l,midd
#define rson root<<1|1,midd+1,r
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int M=2e5+5;
const int mod=998244353;

vector<int>g[M];
int n,m,tot,sz[M],fa[M],id[M],son[M],top[M],dep[M];
int tr[M<<2],lz[M<<2];
int lasans[M],lasans1[M],lasnum[M];
void dfs1(int u,int f){
    sz[u]=1,fa[u]=f;
    dep[u]=dep[f]+1;
    for(auto v:g[u]){
        if(v!=f){
            dfs1(v,u);
            sz[u]+=sz[v];
            if(!son[u]||sz[v]>sz[son[u]])
                son[u]=v;
        }
    }
}
void dfs2(int u,int tp){
    id[u]=++tot,top[u]=tp;
    if(son[u])
        dfs2(son[u],tp);
    for(auto v:g[u]){
        if(v!=fa[u]&&v!=son[u])
            dfs2(v,v);
    }
}
void build(int root,int l,int r){
    tr[root]=lz[root]=0;
    if(l==r)return;
    int midd=(l+r)>>1;
    build(lson);
    build(rson);
}
void up(int root){
    tr[root]=tr[root<<1]+tr[root<<1|1];
}
void pushdown(int root,int l,int r){
    if(lz[root]){
        int midd=(l+r)>>1;
        tr[root<<1]+=(midd-l+1)*2*lz[root];
        tr[root<<1|1]+=(r-midd)*2*lz[root];

        lz[root<<1]+=lz[root];
        lz[root<<1|1]+=lz[root];
        lz[root]=0;
        return ;
    }
}
void update(int L,int R,int root,int l,int r){
    if(L<=l&&r<=R){
        tr[root]+=(r-l+1)*2;
        lz[root]++;
        return ;
    }
    pushdown(root,l,r);
    int midd=(l+r)>>1;
    if(L<=midd)
        update(L,R,lson);
    if(R>midd)
        update(L,R,rson);
    up(root);
}
int query(int L,int R,int root,int l,int r){
    ///cout<<l<<"##"<<r<<endl;
    if(L<=l&&r<=R){
        return tr[root];
    }
    int midd=(l+r)>>1;
    pushdown(root,l,r);
    int res=0;
    if(L<=midd)
        res+=query(L,R,lson);
    if(R>midd)
        res+=query(L,R,rson);
    return res;
}
void solve1(int u){
    while(u){
        update(id[top[u]],id[u],1,1,n);
        u=fa[top[u]];
    }
}
int solve2(int u){
    int ans=0;
    while(u){
        ans+=query(id[top[u]],id[u],1,1,n);
        u=fa[top[u]];
    }
    return ans;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        tot=0;
        for(int i=0;i<=n;i++)
            g[i].clear(),lasans[i]=lasans1[i]=lasnum[i]=0,son[i]=0;
        for(int u,v,i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            g[u].pb(v),g[v].pb(u);
        }
        dfs1(1,0);
        dfs2(1,1);
        build(1,1,n);
        int ans1=0;
        int num=0;///1操作插入的点数
        while(m--){
            int op,x,w;
            scanf("%d%d",&op,&x);
            if(op==1){
                scanf("%d",&w);
                solve1(x);
                ans1+=w-dep[x];
                num++;
            }
            else if(op==2){
                int ans2=solve2(x);
                int ans=lasans[x]+(ans1-lasans1[x])-(num-lasnum[x])*dep[x]+ans2;
                if(ans<=0)
                    lasans[x]=ans-ans2;///保持不变,所以保持前俩项
                else
                    lasans[x]=-ans2;///全不要,而ans2是单独算的,所以在下次算的时候会抵消这次-的
                lasans1[x]=ans1;
                lasnum[x]=num;
            }
            else{
                int ans2=solve2(x);
                int ans=lasans[x]+(ans1-lasans1[x])-(num-lasnum[x])*dep[x]+ans2;
                lasans[x]=ans-ans2;
                lasans1[x]=ans1;
                lasnum[x]=num;
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}
View Code

 

posted @ 2020-08-02 14:53  starve_to_death  阅读(227)  评论(0编辑  收藏  举报