2020牛客暑期多校训练营(第七场)C. A National Pandemic

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

 

题意

给出一颗n个点的树,初始点权为0,执行m次操作

1 x w:给点x的点权加w,其余所有点点权加w-dis(i,x)

2 x:将点x的点权和0取min

3 x:查询x的点权

 

解法一:树链剖分+线段树

对于操作1来说,必须要寻找一种方式,将对所有点的修改进行统一

树上两点间距离很容易想到dis(x,i)=dep(x)+dep(i)-dep(lca)*2,dep表示深度

则w-dis(x,i)=w-dep(x)-dep(i)+dep(lca)*2

其中

w-dep(x)对于所有的点都是一样的,用一个变量记录一下即可

dep(i)是不会改变的,用一个变量记录操作1的次数即可

只有dep(lca)比较难处理

 

假设对如图所示进行操作1 10 5

 

 

红色的数表示每个点实际要加的值

蓝色的数表示dep(lca)

差分的思想,将每个点与它的父节点做差

那就只有从根节点到10号点的路径上,每个点要进行1的修改

dep(lca)*2,就是每个点要进行2的修改

相当于在根节点到10号点的路径上加一个公差为2的等差数列

具体查询某个点的dep(lca)时,查询根节点到这个点的路径上所有点的和即可

即进行的修改相当于差分,然后前缀和求值

给树上的某条路径上所有点加一个值,以及求树上路径点权和,这两种操作树链剖分之后线段树即可

 

对于操作2,查询出该点的点权之后,如果点权>0,记下差值即可

 

对于操作3,用a表示 Σ(w-dep(x)),b表示操作1的次数,del表示操作2的差值,s表示查询结果

答案就是a-b*dep+s-del

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 50001

typedef long long LL;

int n;

int front[N],to[N<<1],nxt[N<<1],tot;

int fa[N],dep[N],siz[N]; 
int id[N],bl[N],dy[N];

LL del[N];

LL sum[N<<2],flag[N<<2];

LL s;

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
}

void dfs1(int x)
{
    siz[x]=1;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x])
        {
            fa[to[i]]=x;
            dep[to[i]]=dep[x]+1;
            dfs1(to[i]);
            siz[x]+=siz[to[i]];
        }
}

void dfs2(int x,int top)
{
    id[x]=++tot;
    dy[tot]=x;
    bl[x]=top;
    int m=0;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x] && siz[to[i]]>siz[m]) m=to[i];
    if(!m) return;
    dfs2(m,top);
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x] && to[i]!=m) dfs2(to[i],to[i]);
}

void down(int k,int l,int mid,int r)
{
    sum[k<<1]+=(mid-l+1)*flag[k];
    sum[k<<1|1]+=(r-mid)*flag[k];
    flag[k<<1]+=flag[k];
    flag[k<<1|1]+=flag[k];
    flag[k]=0;
}

void change(int k,int l,int r,int opl,int opr)
{
    if(l>=opl && r<=opr)
    {
        sum[k]+=r-l+1<<1;
        flag[k]+=2;
        return;
    }    
    int mid=l+r>>1;
    if(flag[k]) down(k,l,mid,r);
    if(opl<=mid) change(k<<1,l,mid,opl,opr);
    if(opr>mid) change(k<<1|1,mid+1,r,opl,opr);
    sum[k]=sum[k<<1]+sum[k<<1|1]; 
}

void Change(int u)
{
    int v=1;
    while(bl[u]!=bl[v])
    {
        if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
        change(1,1,n,id[bl[u]],id[u]);
        u=fa[bl[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    change(1,1,n,id[u],id[v]);
}

void query(int k,int l,int r,int opl,int opr)
{
    if(l>=opl && r<=opr)
    {
        s+=sum[k];
        return;
    }
    int mid=l+r>>1;
    if(flag[k]) down(k,l,mid,r);
    if(opl<=mid) query(k<<1,l,mid,opl,opr);
    if(opr>mid) query(k<<1|1,mid+1,r,opl,opr);
}

void Query(int u)
{
    int v=1;
    s=0;
    while(bl[u]!=bl[v])
    {
        if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
        query(1,1,n,id[bl[u]],id[u]);
        u=fa[bl[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    query(1,1,n,id[u],id[v]);
}

int main()
{
    /* int size = 256 << 20; // 256MB  
    char *p = (char*)malloc(size) + size;  
    __asm__("movl %0, %%esp\n" :: "r"(p)); */
//    freopen("data.txt","r",stdin); 
//    freopen("my.txt","w",stdout);
    int T,m,u,v,op;
    LL a,tmp;
    int b;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        tot=0;
        memset(front,0,sizeof(front));
        for(int i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dep[1]=1;
        dfs1(1);
        tot=0;
        dfs2(1,0);
        a=b=0;
        memset(sum,0,sizeof(sum));
        memset(flag,0,sizeof(flag));
        memset(del,0,sizeof(del));
        while(m--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&u,&v);
                a+=v-dep[u];
                b++;
                Change(u); 
            }
            else
            {
                scanf("%d",&u);
                Query(u);
                tmp=a-1ll*b*dep[u]+s-del[u];
                if(op==2) 
                {
                    if(tmp>0) del[u]+=tmp; 
                }
                else cout<<tmp<<'\n';
            }
        }
    }
}
View Code

 

解法二:点分树

太晚了,明天再补

posted @ 2020-08-17 22:58  TRTTG  阅读(239)  评论(0编辑  收藏  举报