A National Pandemic【树链剖分】

题意

给出一棵 \(n\) 个点的树,每个点 \(x\) 有一个 \(f(x)\) 值,初始全为 \(0\)。现在有三种操作共 \(m\) 次:

  1. 输入:\(x,w\),选择一个点 \(x\) ,对于树上的所有点 \(y\)\(f(y)\) 将增加 \(w-dist(x,y)\)。其中,\(dist(x,y)\) 表示 \(x\)\(y\) 之间最短路径的边数。
  2. 输入:\(x\),更新 \(f(x)\)\(\min(f(x),0)\)
  3. 输入: \(x\) ,询问 \(f(x)\)

\(1\leq n,m \leq 5\times 10^4,0\leq w \leq 10000\)

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

分析

难点在于操作 \(1\) 如何实现,每次修改所有点必然不现实,因此,要想办法将影响保存起来,在查询的时候直接处理。

首先,对于 \(w-dist(x,y)\),可以写成 \((w-depth[x])-(depth[y])+(2\times dpeth[lca(x,y)])\)。对于其中的 \(3\) 部分,分别处理。\((w-depth[x])\) 可以在操作 \(1\) 的时候,一边操作,一边记录。对于 \((depth[y])\) ,只要知道在查询当前点之前进行了多少次操作 \(1\) ,就可以直接算出。对于 \((2\times depth[lca(x,y)])\),只要在每次操作 \(1\) 时,将点 \(x\) 到根节点的路径上所有边的值加 \(2\) ,在查询的时候,只要从查询点向根节点求出路径上的边的权值和即可。

对于 \(2\),将上一次修改为 \(0\) 的结果记录下来,下次查询的时候减去即可。

代码

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=5e4+5;
vector<int>pic[N];
int fa[N],depth[N],num[N];
int top[N],dfn[N],sz[N],son[N];
int tree[N<<2],n,lazy[N<<2];
void dfs1(int u,int p,int d)
{
    depth[u]=d;
    fa[u]=p;
    sz[u]=1;
    son[u]=0;
    for(int i=0;i<pic[u].size();i++)
    {
        int v=pic[u][i];
        if(v==p) continue;
        dfs1(v,u,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])
            son[u]=v;
    }
}
void dfs2(int u,int p,int tp,int &cnt)
{
    top[u]=tp;
    dfn[u]=++cnt;
    if(son[u]==0) return;
    dfs2(son[u],u,tp,cnt);
    for(int i=0;i<pic[u].size();i++)
    {
        int v=pic[u][i];
        if(v==p||v==son[u]) continue;
        dfs2(v,u,v,cnt);
    }
}
void pushup(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void pushdown(int ln,int rn,int rt)
{
    if(lazy[rt])
    {
        tree[rt<<1]+=ln*lazy[rt];
        tree[rt<<1|1]+=rn*lazy[rt];
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        lazy[rt]=0;
    }
}
void build(int l,int r,int rt)
{
    lazy[rt]=0;
    if(l==r)
    {
        tree[rt]=0;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int L,int R,int val,int rt)
{
    if(L<=l&&r<=R)
    {
        tree[rt]+=val*(r-l+1);
        lazy[rt]+=val;
        return;
    }
    int mid=(l+r)>>1;
    pushdown(mid-l+1,r-mid,rt);
    if(L<=mid) update(l,mid,L,R,val,rt<<1);
    if(R>mid) update(mid+1,r,L,R,val,rt<<1|1);
    pushup(rt);
}
int query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R)
        return tree[rt];
    int mid=(l+r)>>1,res=0;
    pushdown(mid-l+1,r-mid,rt);
    if(L<=mid) res+=query(l,mid,L,R,rt<<1);
    if(R>mid) res+=query(mid+1,r,L,R,rt<<1|1);
    return res;
}
void change(int u,int v,int val)
{
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]])
            swap(u,v);
        update(1,n,dfn[top[u]],dfn[u],val,1);
        u=fa[top[u]];
    }
    if(depth[u]<depth[v]) swap(u,v);
    update(1,n,dfn[v],dfn[u],val,1);
}
int ask(int u,int v)
{
    int res=0;
    while(top[u]!=top[v])
    {
        if(depth[top[u]]<depth[top[v]]) swap(u,v);
        res+=query(1,n,dfn[top[u]],dfn[u],1);
        u=fa[top[u]];
    }
    if(depth[u]<depth[v]) swap(u,v);
    res+=query(1,n,dfn[v],dfn[u],1);
    return res;
}
int main()
{
    int T,m;
    scanf("%d",&T);
    while(T--)
    {
        int x,y,cnt=0,sum=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            pic[i].clear();
            num[i]=0;
        }
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            pic[x].pb(y);
            pic[y].pb(x);
        }
        dfs1(1,0,0);
        dfs2(1,0,1,cnt);
        build(1,cnt,1);
        int opt,w,cot=0;
        while(m--)
        {
            scanf("%d",&opt);
            if(opt==1)
            {
                scanf("%d%d",&x,&w);
                change(1,x,2);
                change(1,1,-2);
                cot++;
                sum+=(w-depth[x]);
            }
            else if(opt==2)
            {
                scanf("%d",&x);
                int tmp=sum-depth[x]*cot+ask(1,x)-ask(1,1)-num[x];
                if(tmp>0) num[x]+=tmp;//记录
            }
            else
            {
                scanf("%d",&x);
                int tmp=sum-depth[x]*cot+ask(1,x)-ask(1,1)-num[x];
                printf("%d\n",tmp);
            }
        }
    }
    return 0;
}
posted @ 2020-09-06 09:58  xzx9  阅读(136)  评论(0编辑  收藏  举报