zoukankan      html  css  js  c++  java
  • [HAOI2015]树上操作

    [HAOI2015]树上操作

    2017-09-07


    Description

    有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
    操作,分为三种:
    操作 1 :把某个节点 x 的点权增加 a 。
    操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
    操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

    Input

    第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 
    行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
    第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。 

    Output

    对于每个询问操作,输出该询问的答案。答案之间用换行隔开。 


    Sample Input

    5 5
    1 2 3 4 5
    1 2
    1 4
    2 3
    2 5
    3 3
    1 2 1
    3 5
    2 1 2
    3 3

    Sample Output

    6
    9
    13

    裸的树链剖分.....线段树维护两个dfs建树...mx[i]记录所到子树最大的id编号,方便区间加...因为dfs子树编号都是连续的
    lazy维护区间加,单点加就是自己到自己的区间加.没什么.开始以为树链剖分好难
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    const int maxn=100000+999;
    using namespace std;
    int read(){
        int an=0,f=1;char ch=getchar();
        while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();}
        while('0'<=ch&&ch<='9'){an=an*10+(ch-'0');ch=getchar();}
        return f*an;
    }
    int v[maxn],cnt,fa[maxn],id,belong[maxn],mx[maxn],f[maxn],pos[maxn],deep[maxn];
    int n,m,size[maxn];
    ll tag[maxn*4],sum[maxn*4];
    bool vis[maxn];
    struct sab{
    int nex,to;
    }b[maxn<<1];
    struct saber{
    ll lazy,sum,l,r;
    }tr[maxn*4];
    void add(int x,int y){
        cnt++;b[cnt].nex=f[x];
        f[x]=cnt;b[cnt].to=y;}
    void dfs(int x){
        vis[x]=1;size[x]=1;
        for(int i=f[x];i;i=b[i].nex){
            int v=b[i].to;
            if(!vis[v]){
                fa[v]=x;
                deep[v]=deep[x]+1;
                dfs(v);
                size[x]+=size[v];
            }
        }
    }
    void dfs2(int x,int chain){
        id++;int k=0;
        mx[x]=pos[x]=id;
        belong[x]=chain;
        for(int i=f[x];i;i=b[i].nex){
            int v=b[i].to;
            if(v!=fa[x]&&size[v]>size[k])k=v;}
        
        if(k){dfs2(k,chain);mx[x]=max(mx[x],mx[k]);}
        
        for(int i=f[x];i;i=b[i].nex){
            int v=b[i].to;
            if(v!=fa[x]&&k!=v)dfs2(v,v);
            mx[x]=max(mx[x],mx[v]);
        }
    }
    void pushdown(int l,int r,int k)
    {
        if(l==r)return;
        int mid=(l+r)>>1;ll t=tag[k];tag[k]=0;
        tag[k<<1]+=t;tag[k<<1|1]+=t;
        sum[k<<1]+=t*(mid-l+1);
        sum[k<<1|1]+=t*(r-mid);
    }
    void add(int k,int l,int r,int x,int y,ll val)
    {
        if(tag[k])pushdown(l,r,k);
        if(l==x&&y==r){tag[k]+=val;sum[k]+=(r-l+1)*val;return;}
        int mid=(l+r)>>1;
        if(x<=mid)add(k<<1,l,mid,x,min(mid,y),val);
        if(y>=mid+1)add(k<<1|1,mid+1,r,max(mid+1,x),y,val);
        sum[k]=sum[k<<1]+sum[k<<1|1];
    }
    ll ask(int k,int l,int r,int x,int y)
    {
        if(tag[k])pushdown(l,r,k);
        if(l==x&&y==r)return sum[k];
        int mid=(l+r)>>1;
        ll ans=0;
        if(x<=mid)
            ans+=ask(k<<1,l,mid,x,min(mid,y));
        if(y>=mid+1)
            ans+=ask(k<<1|1,mid+1,r,max(mid+1,x),y);
        return ans;
    }
    ll QUE(int x){
        ll ans=0;
        while(belong[x]!=1){
            ans+=ask(1,1,n,pos[belong[x]],pos[x]);
            x=fa[belong[x]];
        }
        ans+=ask(1,1,n,1,pos[x]);
        return ans;
    }
    int main(){
        n=read();m=read();
        for(int i=1;i<=n;i++)v[i]=read();
        for(int i=1;i<n;i++){
            int x,y;
            x=read();y=read();
            add(x,y);add(y,x);
        }
        dfs(1);
        dfs2(1,1);
        for(int i=1;i<=n;i++)add(1,1,n,pos[i],pos[i],v[i]);
        while(m){
            m--;
            int x,y,z;
            x=read();
            if(x==1){
                y=read();z=read();//将y节点加z 
                add(1,1,n,pos[y],pos[y],z);}
            else if(x==2){
                y=read();z=read();//将y的子树包括他本身+z 
                add(1,1,n,pos[y],mx[y],z);
            }
            else{
                y=read();//现在开始查询从y到1的所有点权和.... 
                printf("%lld
    ",QUE(y));
                }
        }
        return 0;
    }
    树上操作[s酱]

    by:s_a_b_e_r


    树链剖分……

    因为是dfs所以每个子树都是一段连续的区间

    第二遍dfs的时候顺便记录一下从这个点到这个子树结束的区间

    然后就可以把子树加转换为区间加了

    来人,上懒标签……O(∩_∩)O~

    (以及这题可以偷个懒,把单点加写成长度为1的区间加)

    #include<iostream>
    #include<cstdio>
    #define ll long long
    using namespace std;
    const int N=100009;
    int n,m,p[N],cnt,inx;
    int size[N],dep[N],fa[N],bl[N],pos[N],mx[N];
    ll a[N];
    struct node{
    int l,r;
    ll sum,laz;
    }tree[N<<2];
    struct edge{
    int to,nex;
    }e[N<<1];
    void adde(int u,int v)
    {
         ++cnt;
         e[cnt].to=v;
         e[cnt].nex=p[u];
         p[u]=cnt;
    }
    void dfs1(int u)
    {
         size[u]=1;
         for(int i=p[u];i;i=e[i].nex)
         {
           int v=e[i].to;
           if(v==fa[u])continue;
           dep[v]=dep[u]+1;
           fa[v]=u;
           dfs1(v);
           size[u]+=size[v];
         }
    }
    void dfs2(int u,int chain)
    {
         int k=0;++inx;
         pos[u]=mx[u]=inx;
         bl[u]=chain;
         for(int i=p[u];i;i=e[i].nex)
         {
           int v=e[i].to;
           if(dep[u]<dep[v]&&size[k]<size[v])k=v;
         }
         if(k){dfs2(k,chain);mx[u]=max(mx[u],mx[k]);}
         for(int i=p[u];i;i=e[i].nex)
         {
           int v=e[i].to;
           if(dep[u]<dep[v]&&k!=v)
           {dfs2(v,v);mx[u]=max(mx[u],mx[v]);}
         }
    }
    void build(int k,int l,int r)
    {
         tree[k].l=l,tree[k].r=r;
         if(l==r)return;
         int mid=(l+r)>>1;
         build(k<<1,l,mid);
         build(k<<1|1,mid+1,r);
    }
    void add(int k,int p,ll w)
    {
        int l=tree[k].l,r=tree[k].r;
        if(l==r){tree[k].sum+=w;return;}
        int mid=(l+r)>>1;
        if(p<=mid)add(k<<1,p,w);
        else add(k<<1|1,p,w);
        tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
    }
    void addlaz(int k,ll w)
    {
         tree[k].sum+=(tree[k].r-tree[k].l+1)*w;
         tree[k].laz+=w;
    }
    void pushdown(int k)
    {
         addlaz(k<<1,tree[k].laz);
         addlaz(k<<1|1,tree[k].laz);
         tree[k].laz=0;
    }
    void addall(int k,int L,int R,ll w)
    {
         int l=tree[k].l,r=tree[k].r;
         if(r<L||l>R)return;
         if(L<=l&&r<=R)
         {
           tree[k].sum+=(r-l+1)*w;
           tree[k].laz+=w;
           return;
         }
         pushdown(k);
         addall(k<<1,L,R,w);
         addall(k<<1|1,L,R,w);
         tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
    }
    ll ask(int k,int L,int R)
    {
        int l=tree[k].l,r=tree[k].r;
        if(r<L||l>R)return 0;
        if(L<=l&&R>=r)return tree[k].sum;
        pushdown(k);
        return ask(k<<1,L,R)+ask(k<<1|1,L,R);
    }
    ll query(int x)
    {
        ll ans=0;
        while(bl[x]!=1)
        {
          ans+=ask(1,pos[bl[x]],pos[x]);
          x=fa[bl[x]];
        }
        ans+=ask(1,1,pos[x]);
        return ans;
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;++i)cin>>a[i];
        for(int i=1;i<n;++i)
        {
          int x,y;
          scanf("%d%d",&x,&y);
          adde(x,y);adde(y,x);
        }
        dfs1(1);dfs2(1,1);
        build(1,1,n);
        for(int i=1;i<=n;++i)add(1,pos[i],a[i]);
        for(int i=1;i<=n;++i)cout<<pos[i]<<" ";
        cout<<endl;
        while(m--)
        {
          int t,x;ll w;
          cin>>t>>x;
          if(t==1){cin>>w;addall(1,pos[x],pos[x],w);}
          else if(t==2){cin>>w;addall(1,pos[x],mx[x],w);}
          else {cout<<query(x)<<endl;}
        }
        return 0;
    }
    [HAOI2015]树上操作(w菌)

  • 相关阅读:
    swift基础语法(while循环语句)
    swift基础语法(if条件语句)
    swift基础语法(10-字典)
    swift基础语法(08-数组操作)
    swift基础语法(09-数组的其它操作)
    iOS--把网络获取图片进行不变形缩放
    CornerStone简单配置
    mac上搭建svn server
    iOS--利用比例纯代码适配屏幕大小
    iOS--当cell上显示不同数量图片的时候重用
  • 原文地址:https://www.cnblogs.com/ck666/p/7488029.html
Copyright © 2011-2022 走看看