BZOJ 4034"樹上操做"(DFS序+線段樹)

 

傳送門php

 

•題意

  有一棵點數爲 N 的樹,以點 1 爲根,且樹點有邊權。c++

  而後有 M 個操做,分爲三種:ide

    操做 1 :把某個節點 x 的點權增長 a 。
    操做 2 :把某個節點 x 爲根的子樹中全部點的點權都增長 a 。
    操做 3 :詢問某個節點 x 到根的路徑中全部點的點權和。
  輸出操做 3 對應的答案;

•題解

  若是能夠將樹形結構轉化成鏈式結構,那麼,操做 1,2 就能夠用線段樹來維護;ui

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

  定義 $s,e$ 分別記錄每一個節點在序列中第一次出現的位置和最後一次出現的位置;code

  那麼,第一次出現的位置權值爲正,最後一次出現的位置權值爲負;blog

  這樣的話,就能夠經過線段樹來維護;ci

  操做 1 就對應線段樹中的單點更新操做;get

  操做 2 就對應線段樹中的區間更新操做;it

  操做 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\n",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$ 的代碼便可;

相關文章
相關標籤/搜索