2020牛客暑期多校訓練營(第七場)C. A National Pandemic

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

 

題意

給出一顆n個點的樹,初始點權爲0,執行m次操做ide

1 x w:給點x的點權加w,其他全部點點權加w-dis(i,x)spa

2 x:將點x的點權和0取mincode

3 x:查詢x的點權blog

 

解法一:樹鏈剖分+線段樹

對於操做1來講,必需要尋找一種方式,將對全部點的修改進行統一get

樹上兩點間距離很容易想到dis(x,i)=dep(x)+dep(i)-dep(lca)*2,dep表示深度string

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

其中io

w-dep(x)對於全部的點都是同樣的,用一個變量記錄一下便可asm

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

 

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#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

 

解法二:點分樹

太晚了,明天再補

做者: xxy
相關文章
相關標籤/搜索