Codeforces Round #457(Div.2)Problem E Jamie and Tree(DFS序+倍增算法+LCA+树状数组)

E. Jamie and Tree
time limit per test
2.5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

To your surprise, Jamie is the final boss! Ehehehe.

Jamie has given you a tree with n vertices, numbered from 1 to n. Initially, the root of the tree is the vertex with number 1. Also, each vertex has a value on it.

Jamie also gives you three types of queries on the tree:

v — Change the tree's root to vertex with number v.

u v x — For each vertex in the subtree of smallest size that contains u and v, add x to its value.

v — Find sum of values of vertices in the subtree of vertex with number v.

A subtree of vertex v is a set of vertices such that v lies on shortest path from this vertex to root of the tree. Pay attention that subtree of a vertex can change after changing the tree's root.

Show your strength in programming to Jamie by performing the queries accurately!

Input

The first line of input contains two space-separated integers n and q (1 ≤ n ≤ 105, 1 ≤ q ≤ 105) — the number of vertices in the tree and the number of queries to process respectively.

The second line contains n space-separated integers a1, a2, ..., an ( - 108 ≤ ai ≤ 108) — initial values of the vertices.

Next n - 1 lines contains two space-separated integers ui, vi (1 ≤ ui, vi ≤ n) describing edge between vertices ui and vi in the tree.

The following q lines describe the queries.

Each query has one of following formats depending on its type:

v (1 ≤ v ≤ n) for queries of the first type.

u v x (1 ≤ u, v ≤ n,  - 108 ≤ x ≤ 108) for queries of the second type.

v (1 ≤ v ≤ n) for queries of the third type.

All numbers in queries' descriptions are integers.

The queries must be carried out in the given order. It is guaranteed that the tree is valid.

Output

For each query of the third type, output the required answer. It is guaranteed that at least one query of the third type is given by Jamie.

Examples
input
6 7
1 4 2 8 5 7
1 2
3 1
4 3
4 5
3 6
3 1
2 4 6 3
3 4
1 6
2 2 4 -5
1 4
3 3
output
27
19
5
input
4 6
4 3 5 6
1 2
2 3
3 4
3 1
1 3
2 2 4 3
1 1
2 2 4 -3
3 1
output
18
21
Note

The following picture shows how the tree varies after the queries in the first sample.

Codeforces Round #457(Div.2)Problem E Jamie and Tree(DFS序+倍增算法+LCA+树状数组)_第1张图片

【思路】

题目大意是要对任意树根下的子树进行修改、查询操作,开始时根默认为1,后面会改。

类似的子树问题基本上都可以转化成DFS序的区间问题再利用一定的数据结构进行维护,当然可以用线段树,不过这里为了练手我用了能够执行区间修改、区间查询的树状数组,所以写起来比较绕。

在线的情况下,要求LCA,我们可以先用一个稀疏表保存每个节点往上2的若干次方的祖先,显然2的0次方就是它们的父亲。求u和v的LCA,首先调整矮的使之和高的一样高度,然后用倍增算法将二者往上提就可以寻找到它们最低公共祖先了。应注意可能一方是另一方的祖先的情况,这在调整一方高度之后可以先判别掉。

尽管变根,却仍可不动原树,通过分类讨论执行各操作,下面的讨论也都是以1为根为前提的。当前根用r表示。

要求u和v的LCA,有如下三种可能:

1、u和v仅一者在r的子树里,此时LCA是r。

2、u和v两者都在r的子树里,此时可直接算LCA。

3、u和v两者都不在r的子树里,此时应求lca(u, v)、lca(u, r)、lca(v, r),以高度最低的那个为真正的LCA。

对于将处理的子树的根v,将处理的区间有如下三种可能:

1、v就是r,那么就是要动全部区间。

2、v是r的祖先节点,那么可以先处理全部区间,再撤消掉v包含r的那棵子树的那片区间。

3、其他情况,即r是v的祖先节点或者r和v啥关系也没有,直接处理v子树的区间就好。


【代码】

//************************************************************************
// File Name: main.cpp
// Author: Shili_Xu
// E-Mail: [email protected] 
// Created Time: 2018年02月04日 星期日 19时35分40秒
//************************************************************************

#include 
#include 
#include 
using namespace std;

const int MAXN = 1e5 + 5;

struct edge {
	int to, next;
};

int n, q, cnt, root, tot;
int in[MAXN], out[MAXN], mp[MAXN],head[MAXN], deep[MAXN];
int up[MAXN][18];
long long value[MAXN], c[MAXN], d[MAXN];
edge e[2 * MAXN];

int lowbit(int x)
{
	return (x & -x);
}

void modify(long long array[], int x, long long num)
{
	while (x <= n) {
		array[x] += num;
		x += lowbit(x);
	}
}

long long sum(long long array[], int x)
{
	long long ans = 0;
	while (x) {
		ans += array[x];
		x -= lowbit(x);
	}
	return ans;
}

void addedge(int a, int b)
{
	tot++;
	e[tot].to = b;
	e[tot].next = head[a];
	head[a] = tot;
}

void dfs(int u, int fa, int depth)
{
	cnt++;
	in[u] = cnt;
	mp[cnt] = u;
	deep[u] = depth;
	up[u][0] = fa;
	for (int i = head[u]; i; i = e[i].next) {
		int v = e[i].to;
		if (v != fa) dfs(v, u, depth + 1);
	}
	out[u] = cnt;
}

void init()
{
	for (int i = 1; i <= 17; i++)
		for (int j = 1; j <= n; j++)
			up[j][i] = up[up[j][i - 1]][i - 1];
}

int lca(int u, int v)
{
	if (deep[u] < deep[v]) swap(u, v);
	int diff = deep[u] - deep[v];
	for (int i = 17; i >= 0; i--)
		if (diff & (1 << i)) u = up[u][i];
	if (u == v) return u;
	for (int i = 17; i >= 0; i--)
		if (up[u][i] != up[v][i]) u = up[u][i], v = up[v][i];
	return up[u][0];
}

int main()
{
	root = 1; tot = 0;
	memset(head, 0, sizeof(head));
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; i++) scanf("%lld", &value[i]);
	for (int i = 1; i <= n - 1; i++) {
		int u, v;
		scanf("%d %d", &u, &v);
		addedge(u, v);
		addedge(v, u);
	}
	cnt = 0;
	dfs(root, 0, 1);
	modify(c, 1, value[1]);
	d[1] = 0;
	for (int i = 2; i <= n; i++) {	
		modify(c, i, value[mp[i]] - value[mp[i - 1]]);
		modify(d, i, (value[mp[i]] - value[mp[i - 1]]) * (i - 1));
	}
	init();
	for (int i = 1; i <= q; i++) {
		int op;
		scanf("%d", &op);
		if (op == 1)
			scanf("%d", &root);
		if (op == 2) {
			int u, v, p;
			long long x;
			scanf("%d %d %lld", &u, &v, &x);
			if (in[root] <= in[u] && in[u] <= out[root] && in[root] <= in[v] && in[v] <= out[root]) 
				p = lca(u, v);
			else
			if ((in[root] <= in[u] && in[u] <= out[root]) || (in[root] <= in[v] && in[v] <= out[root]))
				p = root;
			else {
				int p1 = lca(u, v), p2 = lca(u, root), p3 = lca(v, root);
				p = (deep[p1] > deep[p2] ? p1 : p2);
				p = (deep[p] > deep[p3] ? p : p3);
			}
			if (root == p ) {
				modify(c, in[1], x);
				modify(c, out[1] + 1, -x);
				modify(d, in[1], x * (in[1] - 1));
				modify(d, out[1] + 1, -x * out[1]);
			}
			else
			if (in[p] <= in[root] && in[root] <= out[p]) {
				int diff = deep[root] - deep[p] - 1, hr = root;
				for (int i = 17; i >= 0; i--)
					if (diff & (1 << i)) hr = up[hr][i];
				modify(c, in[1], x);
				modify(c, out[1] + 1, -x);
				modify(d, in[1], x * (in[1] - 1));
				modify(d, out[1] + 1, -x * out[1]);
				modify(c, in[hr], -x);
				modify(c, out[hr] + 1, x);
				modify(d, in[hr], -x * (in[hr] - 1));
				modify(d, out[hr] + 1, x * out[hr]);
			}
			else {
				modify(c, in[p], x);
				modify(c, out[p] + 1, -x);
				modify(d, in[p], x * (in[p] - 1));
				modify(d, out[p] + 1, -x * out[p]);
			}	
		}
		if (op == 3) {
			int p;
			scanf("%d", &p);
			long long ans;
			if (root == p )
				ans = sum(c, n) * n - sum(d, n);
			else
			if (in[p] <= in[root] && in[root] <= out[p]) {
				int diff = deep[root] - deep[p] - 1, hr = root;
				for (int i = 17; i >= 0; i--)
					if (diff & (1 << i)) hr = up[hr][i];
				ans = sum(c, n) * n - sum(d, n);
				ans -= ((sum(c, out[hr]) * out[hr] - sum(d, out[hr])) - (sum(c, in[hr] - 1) * (in[hr] - 1) - sum(d, in[hr] - 1)));
			}
			else
				ans = (sum(c, out[p]) * out[p] - sum(d, out[p])) - (sum(c, in[p] - 1) * (in[p] - 1) - sum(d, in[p] - 1));
			printf("%lld\n", ans);
		}
	}
	return 0;
}



你可能感兴趣的:(树状数组,LCA)