zoukankan      html  css  js  c++  java
  • 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
    " :: "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<<'
    ';
                }
            }
        }
    }
    View Code

    解法二:点分树

    太晚了,明天再补

  • 相关阅读:
    rman进行备份、恢复
    [每日一题] 11gOCP 1z0-053 :2013-10-7 the backup of MULT_DATA................................32
    容器可以简化图形化界面的设计,以整体结构来布置界面
    组件(Conponent)是图形用户界面最基本的部分
    java.awt包提供了基本的java程序的GUI设计工具
    Window对应的类为java.awt.Windows, 它可独立于其他Container而存在
    Container类是Component的子类,它也是一个抽象类,它允许其他的组件(Component)加入其中
    AWT从概念产生到完成实现只用了一个月
    Java释出的时候,AWT作为Java最弱的组件受到不小的批评
    抽象窗口工具包AWT (Abstract Window Toolkit) 是 API为Java 程序提供的建立 图形用户界面
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/13520714.html
Copyright © 2011-2022 走看看