hdu3966-线段树
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3966
Description
Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of enemies who want to invade his kingdom. As Aragorn knows, the enemy has N camps out of his kingdom and M edges connect them. It is guaranteed that for any two camps, there is one and only one path connect them. At first Aragorn know the number of enemies in every camp. But the enemy is cunning , they will increase or decrease the number of soldiers in camps. Every time the enemy change the number of soldiers, they will set two camps C1 and C2. Then, for C1, C2 and all camps on the path from C1 to C2, they will increase or decrease K soldiers to these camps. Now Aragorn wants to know the number of soldiers in some particular camps real-time.
Input
Multiple test cases, process to the end of input.
For each case, The first line contains three integers N, M, P which means there will be N(1 ≤ N ≤ 50000) camps, M(M = N-1) edges and P(1 ≤ P ≤ 100000) operations. The number of camps starts from 1.
The next line contains N integers A1, A2, …AN(0 ≤ Ai ≤ 1000), means at first in camp-i has Ai enemies.
The next M lines contains two integers u and v for each, denotes that there is an edge connects camp-u and camp-v.
The next P lines will start with a capital letter ‘I’, ‘D’ or ‘Q’ for each line.
‘I’, followed by three integers C1, C2 and K( 0≤K≤1000), which means for camp C1, C2 and all camps on the path from C1 to C2, increase K soldiers to these camps.
‘D’, followed by three integers C1, C2 and K( 0≤K≤1000), which means for camp C1, C2 and all camps on the path from C1 to C2, decrease K soldiers to these camps.
‘Q’, followed by one integer C, which is a query and means Aragorn wants to know the number of enemies in camp C at that time.
Output
For each query, you need to output the actually number of enemies in the specified camp.
Sample Input
3 2 5
1 2 3
2 1
2 3
I 1 3 5
Q 2
D 1 2 2
Q 1
Q 3
Sample Output
7
4
8
題意
有n個營地,被m條路連接,p次詢問,每次詢問有三種情況:
I C1 C2 K,讓C1營地到C2營地之間所有營地增加K個人
D C1 C2 K ,讓C1營地到C2營地之間所有營地減少K個人
Q C 詢問C營地當前人數
思路
第一道樹鏈剖分感覺還蠻好的,開7個數組作用如下:siz保存以i為根的子樹節點個數,top保存i節點所在鏈的頂端節點,son保存i節點的重兒子,fa保存i節點的父親節點,dep保存i節點的深度(根為1),,tid保存i節點dfs后的新編號,rak保存新編號i對應的節點(rak[i]=j,tid[j]=i)。
第一次dfs可以得到siz,son,fat,dep四個數組,第二次可以得到tid,rak,top三個數組,這時如果我們要更新(x,y)營地,可以通過類似LCA倍增的做法,每次更新深度較低的點到該點的top點之間的數據,依次更新。用線段樹完成更新操作,樹鏈剖分更多的是讓樹上兩個點可以通過類似LCA的方式找到路徑。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | using namespace std; typedef long long ll; const int maxn = 51000; struct node { int s, e, next; }edge[maxn * 2]; int n, m, p; int son[maxn], top[maxn], tid[maxn], fat[maxn], siz[maxn], dep[maxn], rak[maxn]; int head[maxn], len, dfx; //siz保存以i為根的子樹節點個數,top保存i節點所在鏈的頂端節點,son保存i節點的重兒子,fa保存i節點的父親節點\ dep保存i節點的深度(根為1),,tid保存i節點dfs后的新編號,rk保存新編號i對應的節點(rak[i]=j,tid[j]=i)。 void dfs1(int x, int fa, int d) { siz[x] = 1, dep[x] = d; fat[x] = fa; son[x] = -1; for (int i = head[x]; i != -1; i = edge[i].next) { int y = edge[i].e; if (y == fa) continue; dfs1(y, x, d + 1); siz[x] += siz[y]; if (son[x] == -1 || siz[y] > siz[son[x]]) son[x] = y; } } void dfs2(int x, int c) { top[x] = c; tid[x] = ++dfx; rak[dfx] = x; if (son[x]==-1) return; dfs2(son[x], c); for (int i = head[x]; i != -1; i = edge[i].next) { int y = edge[i].e; if (y == fat[x] || y == son[x]) continue; dfs2(y, y); } } void add(int s, int e) { edge[len].e = e; edge[len].next = head[s]; head[s] = len++; } void init() { memset(head, -1, sizeof(head)); len = 0; dfx = 0; } ll sum[maxn * 4]; ll lazy[maxn * 4]; ll a[maxn * 4]; void up(int i) { sum[i] = sum[i << 1] + sum[i << 1 | 1]; } void down(int i, int len) { if (lazy[i]) { lazy[i << 1] += lazy[i]; lazy[i << 1 | 1] += lazy[i]; sum[i << 1] += lazy[i] * (len - (len >> 1)); sum[i << 1 | 1] += lazy[i] * (len >> 1); lazy[i] = 0; } } void build(int l, int r, int i) { lazy[i] = 0; if (l == r) { sum[i] = a[rak[l]]; return; } int mid = (l + r) / 2; build(lson); build(rson); up(i); } void update(int L, int R, int k, int l, int r, int i) { if (L <= l && r <= R) { sum[i] += (r - l + 1)*k; lazy[i] += k; return; } down(i, r - l + 1); int mid = (l + r) / 2; if (L <= mid) update(L, R, k, lson); if (R > mid) update(L, R, k, rson); up(i); } ll query(int k, int l, int r, int i) { if (l == r) { return sum[i]; } down(i, r - l + 1); int mid = (l + r) / 2; int ans = 0; if (k <= mid) ans += query(k, lson); else ans += query(k, rson); return ans; } void setupdate(int x, int y, int k) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x, y); update(tid[top[x]], tid[x], k, 1, n, 1); x = fat[top[x]]; } if (dep[x] < dep[y]) swap(x, y); update(tid[y], tid[x], k, 1, n, 1); } int main() { while (scanf("%d%d%d", &n, &m, &p) != EOF) { init(); int x, y, z; for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= m; i++) { scanf("%d%d", &x, &y); add(x, y); add(y, x); } dfs1(1, 0, 0); dfs2(1, 1); build(1, n, 1); while (p--) { char s[2]; scanf("%s", s); if (s[0] == 'Q') { scanf("%d", &x); printf("%d\n", query(tid[x], 1, n, 1)); } else { scanf("%d%d%d", &x, &y, &z); if (s[0] == 'D') z = -z; setupdate(x, y, z); } } } } |
謝謝你請我吃糖果
總結
以上是生活随笔為你收集整理的hdu3966-线段树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大写金额转换(报销大写金额转换)
- 下一篇: 怎么解决百度快照劫持咋办咋解决 、百度快