zoukankan      html  css  js  c++  java
  • BZOJ 4034"树上操作"(DFS序+线段树)

    传送门

    •题意

      有一棵点数为 N 的树,以点 1 为根,且树点有边权。

      然后有 M 个操作,分为三种:

        操作 1 :把某个节点 x 的点权增加 a 。
        操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
        操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
      输出操作 3 对应的答案;

    •题解

      如果可以将树形结构转化成链式结构,那么,操作 1,2 就可以用线段树来维护;

      $1,2,4,4,5,5,2,3,3,1$

      定义 $s,e$ 分别记录每个节点在序列中第一次出现的位置和最后一次出现的位置;

      那么,第一次出现的位置权值为正,最后一次出现的位置权值为负;

      这样的话,就可以通过线段树来维护;

      操作 1 就对应线段树中的单点更新操作;

      操作 2 就对应线段树中的区间更新操作;

      操作 3 就是区间查询操作;

    •Code

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define ll long long
      4 #define ls(x) (x<<1)
      5 #define rs(x) (x<<1|1)
      6 #define mem(a,b) memset(a,b,sizeof(a))
      7 const int maxn=1e5+50;
      8 
      9 int n,m;
     10 int a[maxn];
     11 int num;
     12 int head[maxn];
     13 struct Edge
     14 {
     15     int to;
     16     int next;
     17 }G[maxn<<1];
     18 void addEdge(int u,int v)
     19 {
     20     G[num]={v,head[u]};
     21     head[u]=num++;
     22 }
     23 struct Seg
     24 {
     25     int l,r;
     26     ll lazy;
     27     ll sum;
     28     int x;///沿叶子方向的节点个数
     29     int y;///沿根方向的节点个数
     30     int mid(){return l+((r-l)>>1);}
     31     void Set(ll val)
     32     {
     33         ///如果[l,r]区间的每个节点都增加val
     34         ///那么,[l,r]区间中沿叶子方向的x个节点增加+val
     35         ///[l,r]区间中沿根方向的y个节点增加-val
     36         lazy += val;
     37         sum += 1ll*(x-y)*val;
     38     }
     39 }seg[maxn<<3];
     40 int cnt;
     41 int s[maxn];
     42 int e[maxn];
     43 int vs[maxn<<1];
     44 bool g[maxn<<1];
     45 
     46 void DFS(int u,int f)
     47 {
     48     vs[++cnt]=u;
     49     s[u]=cnt;
     50     g[cnt]=1;
     51     for(int i=head[u];~i;i=G[i].next)
     52     {
     53         int v=G[i].to;
     54         if(v != f)
     55             DFS(v,u);
     56     }
     57     vs[++cnt]=u;
     58     e[u]=cnt;
     59     g[cnt]=0;
     60 }
     61 void pushUp(int pos)
     62 {
     63     seg[pos].x=seg[ls(pos)].x+seg[rs(pos)].x;
     64     seg[pos].y=seg[ls(pos)].y+seg[rs(pos)].y;
     65     seg[pos].sum=seg[ls(pos)].sum+seg[rs(pos)].sum;
     66 }
     67 void pushDown(int pos)
     68 {
     69     ll &lazy=seg[pos].lazy;
     70     if(!lazy)
     71         return ;
     72 
     73     seg[ls(pos)].Set(lazy);
     74     seg[rs(pos)].Set(lazy);
     75 
     76     lazy=0;
     77 }
     78 void build(int l,int r,int pos)
     79 {
     80     seg[pos]={l,r,0,0,0,0};
     81 
     82     if(l == r)
     83     {
     84         if(g[l])
     85         {
     86             seg[pos].x++;
     87             seg[pos].sum=a[vs[l]];
     88         }
     89         else
     90         {
     91             seg[pos].y++;
     92             seg[pos].sum=-a[vs[l]];
     93         }
     94 
     95         return ;
     96     }
     97 
     98     int mid=seg[pos].mid();
     99     build(l,mid,ls(pos));
    100     build(mid+1,r,rs(pos));
    101 
    102     pushUp(pos);
    103 }
    104 void update(int pos,int l,int r,int x)
    105 {
    106     if(seg[pos].l == l && seg[pos].r == r)
    107     {
    108         seg[pos].Set(x);
    109         return ;
    110     }
    111     pushDown(pos);
    112 
    113     int mid=seg[pos].mid();
    114     if(r <= mid)
    115         update(ls(pos),l,r,x);
    116     else if(l > mid)
    117         update(rs(pos),l,r,x);
    118     else
    119     {
    120         update(ls(pos),l,mid,x);
    121         update(rs(pos),mid+1,r,x);
    122     }
    123     pushUp(pos);
    124 }
    125 ll query(int pos,int l,int r)
    126 {
    127     if(seg[pos].l == l && seg[pos].r == r)
    128         return seg[pos].sum;
    129     pushDown(pos);
    130 
    131     int mid=seg[pos].mid();
    132     if(r <= mid)
    133         return query(ls(pos),l,r);
    134     else if(l > mid)
    135         return query(rs(pos),l,r);
    136     else
    137         return query(ls(pos),l,mid)+query(rs(pos),mid+1,r);
    138 }
    139 void Solve()
    140 {
    141     cnt=0;
    142     DFS(1,1);
    143     build(1,cnt,1);
    144 
    145     while(m--)
    146     {
    147         int op;
    148         scanf("%d",&op);
    149         if(op == 1)
    150         {
    151             int u,x;
    152             scanf("%d%d",&u,&x);
    153             update(1,s[u],s[u],x);
    154             update(1,e[u],e[u],x);
    155         }
    156         else if(op == 2)
    157         {
    158             int u,x;
    159             scanf("%d%d",&u,&x);
    160             update(1,s[u],e[u],x);
    161         }
    162         else
    163         {
    164             int u;
    165             scanf("%d",&u);
    166             printf("%lld
    ",query(1,s[1],s[u]));
    167         }
    168     }
    169 }
    170 void Init()
    171 {
    172     num=0;
    173     mem(head,-1);
    174 }
    175 int main()
    176 {
    177 //    freopen("C:\Users\hyacinthLJP\Desktop\C++WorkSpace\in&&out\contest","r",stdin);
    178     scanf("%d%d",&n,&m);
    179     for(int i=1;i <= n;++i)
    180         scanf("%d",a+i);
    181 
    182     Init();
    183     for(int i=1;i < n;++i)
    184     {
    185         int u,v;
    186         scanf("%d%d",&u,&v);
    187         addEdge(u,v);
    188         addEdge(v,u);
    189     }
    190     Solve();
    191 
    192     return 0;
    193 }
    View Code

    •变形

      此题操作 3 还可改成求解 $u,v$ 路径间的节点权值和;

      只需在原来的基础上增加个求解 $LCA$ 的代码即可;

  • 相关阅读:
    UML类图和用例图
    设计模式基本原则
    c# 协变和逆变
    git本地忽略
    计算器科学概论-数据操控
    计算机科学概论-数据存储
    docker部署gitlab-ce
    sqlserver2008R2 本地不能用localhost连接
    Redis常用命令
    C# 值类型和引用类型的区别
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11783072.html
Copyright © 2011-2022 走看看