傳送門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
View Code1 #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 }
•變形
此題操做 3 還可改爲求解 $u,v$ 路徑間的節點權值和;
只需在原來的基礎上增長個求解 $LCA$ 的代碼便可;