2020牛客暑期多校训练营(第七场) C、A National Pandemic(树链剖分)

题目链接

题面:
在这里插入图片描述

题意:
给定一棵 n , n ≤ 5 e 4 n,n\le5e4 n,n5e4 个节点的树,初始时节点权值均为0。
m , m ≤ 5 e 4 m,m\le5e4 m,m5e4个操作。

每个操作可以:
(1)选择一个中心节点 x x x,并将所有的节点 y y y 的权值加上 w − d i s ( x , y ) w-dis(x,y) wdis(x,y)
(2)将 x x x 节点的 v a l ( x ) = m i n ( v a l ( x ) , 0 ) val(x)=min(val(x),0) val(x)=min(val(x),0)
(3)查询节点 x x x 的权值 v a l ( x ) val(x) val(x)

题解:
①、
对于操作(2),我们可以维护一个增量数组 d e l t a delta delta
通俗的说,如果当前 x x x 节点的权值 v a l ( x ) < = 0 val(x)<=0 val(x)<=0,那么 d e l t a ( x ) delta(x) delta(x) 不变。
如果当前的 x x x 节点的权值 v a l ( x ) > 0 val(x)>0 val(x)>0,那么 d e l t a ( x ) + = − v a l ( x ) delta(x)+=-val(x) delta(x)+=val(x),表示 x x x 节点的权值应该在当前权值上再加上 d e l t a ( x ) delta(x) delta(x) 才是取 m i n min min之后的值。
即:每次查询 x x x 节点的权值的时候,都要加上 d e l t a ( x ) delta(x) delta(x)

②、
对于操作(1),我们可以发现相当于所有节点 y y y 都加上了 w − d i s ( x , y ) = w − ( d [ x ] + d [ y ] − 2 ∗ d [ l c a ( x , y ) ] ) = w − d [ x ] − d [ y ] + 2 ∗ d [ l c a ( x , y ) ] w-dis(x,y)=w-(d[x]+d[y]-2*d[lca(x,y)])=w-d[x]-d[y]+2*d[lca(x,y)] wdis(x,y)=w(d[x]+d[y]2d[lca(x,y)])=wd[x]d[y]+2d[lca(x,y)]

我们可以发现 w − d [ x ] w-d[x] wd[x] 这一部分的贡献对于每一个点都是一样的,我们可以单独用个全局变量 a n s ans ans 记录。

我们将 x − r o o t x-root xroot 这条链上的点的权值都加上2,我们查询某一节点 y y y 时, y − r o o t y-root yroot 的权值和就是 2 ∗ d [ l c a ( x , y ) ] 2*d[lca(x,y)] 2d[lca(x,y)]。(相当于先标记了 x − r o o t x-root xroot 链,再去标记 y − r o o t y-root yroot 链,这样重合的节点就是 ( x , y ) (x,y) (x,y) 的公共祖先)。

最后我们还剩下一部分 − d [ y ] -d[y] d[y] ,我们发现有多少次操作(1),那么 − d [ y ] -d[y] d[y] 就贡献了多少次,记录一下操作(1) 的次数即可。

在询问 x x x 点的权值的时候。
v a l ( x ) = a n s − d [ x ] ∗ n u m + a s k r o a d ( 1 , x ) + d e l t a ( x ) val(x)=ans-d[x]*num+askroad(1,x)+delta(x) val(x)=ansd[x]num+askroad(1,x)+delta(x)

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#include<list>
#include<ctime>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-1;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=50100;
const int maxp=400100;
const int maxm=2100;
const int up=200000;

int head[maxn],ver[maxn<<1],nt[maxn<<1],tot=1;
int f[maxn],d[maxn],si[maxn],son[maxn],cnt=0;
int rk[maxn],top[maxn],id[maxn];
ll delta[maxn],ans=0,num=0;

void init(int n)
{
    tot=1,cnt=0;
    for(int i=1;i<=n;i++)
        delta[i]=head[i]=son[i]=0;
    ans=0,num=0;
}

void  add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}

void dfs1(int x,int fa)
{
    int maxson=0;
    si[x]=1,d[x]=d[fa]+1;
    for(int i=head[x];i;i=nt[i])
    {
        int y=ver[i];
        if(y==fa) continue;
        f[y]=x;
        dfs1(y,x);
        si[x]+=si[y];
        if(si[y]>maxson) maxson=si[y],son[x]=y;
    }
}

void dfs2(int x,int t)
{
    top[x]=t;id[x]=++cnt;rk[cnt]=x;
    if(!son[x]) return ;
    dfs2(son[x],t);
    for(int i=head[x];i;i=nt[i])
    {
        int y=ver[i];
        if(y!=son[x]&&y!=f[x])
            dfs2(y,y);
    }
}

struct node
{
    int l,r;
    ll sum,laz;
}t[maxn<<2];

void pushup(int cnt)
{
    t[cnt].sum=t[lc].sum+t[rc].sum;
}

void pushdown(int cnt)
{
    if(t[cnt].laz)
    {
        t[lc].laz+=t[cnt].laz;
        t[rc].laz+=t[cnt].laz;
        t[lc].sum+=t[cnt].laz*len(lc);
        t[rc].sum+=t[cnt].laz*len(rc);
        t[cnt].laz=0;
    }
}

void build(int l,int r,int cnt)
{
    t[cnt].l=l,t[cnt].r=r;
    t[cnt].laz=t[cnt].sum=0;
    if(l==r) return ;
    build(l,tmid,lc);
    build(tmid+1,r,rc);
}

void change(int l,int r,int cnt,ll val)
{
    if(l<=t[cnt].l&&t[cnt].r<=r)
    {
        t[cnt].sum+=val*len(cnt);
        t[cnt].laz+=val;
        return ;
    }
    pushdown(cnt);
    if(t[lc].r>=l) change(l,r,lc,val);
    if(t[rc].l<=r) change(l,r,rc,val);
    pushup(cnt);
}

ll ask(int l,int r,int cnt)
{
    if(l<=t[cnt].l&&t[cnt].r<=r)
    {
        return t[cnt].sum;
    }
    pushdown(cnt);
    ll ans=0;
    if(t[lc].r>=l) ans+=ask(l,r,lc);
    if(t[rc].l<=r) ans+=ask(l,r,rc);
    return ans;
}

void changeroad(int x,int y,ll val)
{
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
            swap(x,y);
        change(id[top[x]],id[x],1,val);
        x=f[top[x]];
    }
    if(d[x]>d[y]) swap(x,y);
    change(id[x],id[y],1,val);
}

ll askroad(int x,int y)
{
    ll ans=0;
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])
            swap(x,y);
        ans+=ask(id[top[x]],id[x],1);
        x=f[top[x]];
    }
    if(d[x]>d[y]) swap(x,y);
    ans+=ask(id[x],id[y],1);
    return ans;
}

ll askforval(int x)
{
    return ans-d[x]*num+askroad(1,x)+delta[x];
}

int main(void)
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        int n,m,x,y,w,op;
        scanf("%d%d",&n,&m);
        init(n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }

        dfs1(1,0);
        dfs2(1,1);
        build(1,cnt,1);

        for(int i=1;i<=m;i++)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&x,&w);
                ans+=w-d[x];
                changeroad(1,x,2);
                num++;
            }
            else if(op==2)
            {
                scanf("%d",&x);
                delta[x]-=max(0,askforval(x));
            }
            else if(op==3)
            {
                scanf("%d",&x);
                printf("%lld\n",askforval(x));
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值