#Light oj - 1348 Aladdin and the Return Journey (LCA + 樹上線段樹)

Finally the Great Magical Lamp was in Aladdin's hand. Now he wanted to return home. But he didn't want to take any help from the Genie because he thought that it might be another adventure for him. All he remembered was the paths he had taken to reach there. But since he took the lamp, all the genies in the cave became angry and they were planning to attack. As Aladdin was not afraid, he wondered how many genies were there. He summoned the Genie from the lamp and asked this.

Now you are given a similar problem. For simplicity assume that, you are given a tree (a connected graph with no cycles) with n nodes, nodes represent places, edges represent roads. In each node, initially there are an arbitrary number of genies. But the numbers of genies change in time. So, you are given a tree, the number of genies in each node and several queries of two types. They are:

  1. 0 i j, it means that you have to find the total number of genies in the nodes that occur in path from node i to j (0 ≤ i, j < n).
  2. 1 i v, it means that number of genies in node i is changed to v (0 ≤ i < n, 0 ≤ v ≤ 1000).

Input

Input starts with an integer T (≤ 10), denoting the number of test cases.

Each case starts with a blank line. Next line contains an integer n (2 ≤ n ≤ 30000). The next line contains n space separated integers between 0 and 1000, denoting the number of genies in the nodes respectively. Then there are n-1 lines each containing two integers: u v (0 ≤ u, v < n, u ≠ v) meaning that there is an edge from node u and v. Assume that the edges form a valid tree. Next line contains an integer q (1 ≤ q ≤ 105) followed by q lines each containing a query as described above.

Output

For each case, print the case number in a single line. Then for each query 0 i j, print the total number of genies in the nodes that occur in path i to j.

Sample Input

1

 

4

10 20 30 40

0 1

1 2

1 3

3

0 2 3

1 1 100

0 2 3

Sample Output

Case 1:

90

170

Note

Dataset is huge, use faster I/O methods.

題目大意 : 有一個帶權樹, 現在有兩種操作, 一是將某個點的權值改爲 W, 二是輸出兩個點之間路徑上的所有點權和

思路 : 正解是樹鏈剖分, 但是也可以不用,先將樹上的關係轉換成線性, 再用線段樹維護每個點到點 1 的點權和, 區間修改, 單點查詢,查詢的答案就是Q(U) + Q(V) - Q(lca) - Q(fa[lca]), 想想就明白了,和樹上差分一樣。 注意特判父節點爲0的情況,直接返回0

Accepted code

#include<iostream>
#include<algorithm>
#include<functional>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 3e4 + 100;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }

struct Tree
{
	int l, r, ans, lzy;
}t[MAXN * 4];
vector <int> e[MAXN << 1];
int p[MAXN][25], dep[MAXN], in[MAXN], out[MAXN];
int num[MAXN], n, m, tot, T, X;
int val[MAXN];
void init() {
	for (int i = 1; i <= n; i++) e[i].clear();
	MEM(dep, 0); MEM(in, 0); MEM(out, 0); MEM(num, 0);
	tot = 0; MEM(p, 0);
}
void Build(int rt, int l, int r) {
	t[rt].l = l, t[rt].r = r;
	t[rt].ans = 0, t[rt].lzy = 0;
	if (l == r) return;
	int mid = (l + r) >> 1;
	Build(ls, l, mid);
	Build(rs, mid + 1, r);
}
void Pushdown(int rt) {
	t[ls].lzy += t[rt].lzy, t[rs].lzy += t[rt].lzy;
	t[ls].ans += t[rt].lzy, t[rs].ans += t[rt].lzy;
	t[rt].lzy = 0;
}
void Update(int rt, int l, int r, int pos) {
	if (t[rt].l >= l && t[rt].r <= r) {
		t[rt].ans += pos, t[rt].lzy += pos;
		return;
	}
	int mid = (t[rt].l + t[rt].r) >> 1;
	if (t[rt].lzy) Pushdown(rt);
	if (mid < l) Update(rs, l, r, pos);
	else if (mid >= r) Update(ls, l, r, pos);
	else {
		Update(ls, l, mid, pos);
		Update(rs, mid + 1, r, pos);
	}
	t[rt].ans = t[ls].ans + t[rs].ans;
}
int Query(int rt, int x) {
	if (x == 0) return 0;
	if (t[rt].l == x && t[rt].r == x) return t[rt].ans;
	int mid = (t[rt].l + t[rt].r) >> 1; int ui = 0, vi = 0;
	if (t[rt].lzy) Pushdown(rt);
	if (mid >= x) ui = Query(ls, x);
	else vi = Query(rs, x);
	return ui + vi;
}
void dfs(int x, int fa) {
	dep[x] = dep[fa] + 1, p[x][0] = fa;
	num[x] = ++tot, in[num[x]] = tot;
	for (int i = 1; (1 << i) <= dep[x]; i++)
		p[x][i] = p[p[x][i - 1]][i - 1];
	for (int i = 0; i < SZ(e[x]); i++) {
		int vi = e[x][i];
		if (vi != fa) dfs(vi, x);
	}
	out[num[x]] = tot;
}
int LCA(int xi, int yi) {
	if (dep[xi] > dep[yi]) swap(xi, yi);
	for (int i = 20; i >= 0; i--) {
		if (dep[yi] - (1 << i) >= dep[xi])
			yi = p[yi][i];
	}
	if (xi == yi) return xi;
	for (int i = 20; i >= 0; i--) {
		if (p[xi][i] == p[yi][i]) continue;
		xi = p[xi][i], yi = p[yi][i];
	}
	return p[xi][0];
}

int main()
{
	cin >> T;
	while (T--) {
		sc("%d", &n); init(); Build(1, 1, n);
		for (int i = 1; i <= n; i++) sc("%d", &val[i]);  // 點權
		for (int i = 1; i < n; i++) {
			int ui, vi; sc("%d %d", &ui, &vi);
			ui++, vi++;
			e[ui].push_back(vi);
			e[vi].push_back(ui);
		}
		dfs(1, 0); cin >> m;
		printf("Case %d:\n", ++X);  // 每個點的子樹權值
		for (int i = 1; i <= n; i++) Update(1, in[num[i]], out[num[i]], val[i]);
		for (int i = 0; i < m; i++) {
			int op, ui, vi; sc("%d %d %d", &op, &ui, &vi);
			if (op == 1) {
				ui++;  // 更新
				Update(1, in[num[ui]], out[num[ui]], vi - val[ui]); 
				val[ui] = vi;
			}
			else {
				ui++, vi++;
				int lca = LCA(ui, vi);
				printf("%d\n", Query(1, num[ui]) + Query(1, num[vi]) - Query(1, num[lca]) - Query(1, num[p[lca][0]]));  // 查詢
			}
		}
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章