HDU 4718 The LCIS on the Tree 树上路径倍增



The LCIS on the Tree

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)


Problem Description
For a sequence S 1, S 2, ... , S N, and a pair of integers (i, j), if 1 <= i <= j <= N and S i < S i+1 < S i+2 < ... < S j-1 < S j , then the sequence S i, S i+1, ... , S j is a CIS (Continuous Increasing Subsequence). The longest CIS of a sequence is called the LCIS (Longest Continuous Increasing Subsequence).
Now we consider a tree rooted at node 1. Nodes have values. We have Q queries, each with two nodes u and v. You have to find the shortest path from u to v. And write down each nodes' value on the path, from u to v, inclusive. Then you will get a sequence, and please show us the length of its LCIS.
 

Input
The first line has a number T (T <= 10) , indicating the number of test cases.
For each test case, the first line is a number N (N <= 10 5), the number of nodes in the tree.
The second line comes with N numbers v1, v2, v3 ... , v N, describing the value of node 1 to node N. (1 <= v i <= 10 9)
The third line comes with N - 1 numbers p 2, p 3, p 4 ... , p N, describing the father nodes of node 2 to node N. Node 1 is the root and will have no father.
Then comes a number Q, it is the number of queries. (Q <= 10 5)
For next Q lines, each with two numbers u and v. As described above.
 

Output
For test case X, output "Case #X:" at the first line.
Then output Q lines, each with an answer to the query.
There should be a blank line *BETWEEN* each test case.
 

Sample Input
 
   
1 5 1 2 3 4 5 1 1 3 3 3 1 5 4 5 2 5
 

Sample Output
 
   
Case #1: 3 2 3
 

Source
2013 ACM/ICPC Asia Regional Online —— Warmup2

题意:给定一个节点数小于10w的树,每个节点有一个点权。再给出Q次询问(小于10w),每次给定一个起点和终点u,v。求在u点到v点的最短路上,节点点权的最长上连续升子段的长度。
思路:
    除了数链剖分,动态树等方法之外,此题还可采用树上路径倍增的方法来解决。分别维护以某个节点,往上走2^i步之后的区间内的一些信息。这里,假设这个区间是有方向的,就是以上述的“某个”节点为起点,以上述的”往上走2^i步之后“到达的点为终点。用LL[0],LL[1]分别表示这个区间从起点(定义为左端点)开始最长连续的上升、下降子段长度,RR[0]、RR[1]表示终点为区间右端点的最长的连续上升、下降的子段长度。用ans[0]、ans[1]分别维护这个区间内连续上升、下降的长度。特别注意,这里说的上升下降,是在规定了区间起点和终点的情况下说的!
    对于每一次询问,先求出u,v的lca。lca也用树上路径倍增的方法来维护和求得。维护的到u-lca段上的右连续上升和这个区间内的最长连续上升段的长度,维护得到v-lca的右连续下降长度和这个区间内的最长下降段的长度。对于lca-v这区间,颠倒起点终点之后,维护的值都代表上升的长度啦!最后答案就是u-lca的答案,v-lca的答案,两个右连续的和,这三个值取最大值。
#include
#include
#include
using namespace std;
#define MAXN 100005
int anc[MAXN][17];
int LL[MAXN][17][2], RR[MAXN][17][2];
int ans[MAXN][17][2];
int dep[MAXN];
struct EDGE
{
	EDGE() {}
	EDGE(int _to, int _next) { to = _to, next = _next; }
	int to, next;
}edge[MAXN];
int edgecnt, head[MAXN];
void init()
{
	memset(anc, -1, sizeof(anc));
	memset(head, -1, sizeof(head));
	edgecnt = 0;
}
void add(int s, int t)
{
	edge[edgecnt] = EDGE(t, head[s]);
	head[s] = edgecnt++;
}
int val[MAXN];
void dfs(int u, int fa, int deep)
{
	anc[u][0] = fa;
	dep[u] = deep;
	if (val[u] < val[fa])
	{
		LL[u][0][0] = 2;
		LL[u][0][1] = 1;
		RR[u][0][0] = 2;
		RR[u][0][1] = 1;
		ans[u][0][0] = 2;
		ans[u][0][1] = 1;
	}
	else if (val[u]>val[fa])
	{
		LL[u][0][0] = 1;
		LL[u][0][1] = 2;
		RR[u][0][0] = 1;
		RR[u][0][1] = 2;
		ans[u][0][0] = 1;
		ans[u][0][1] = 2;
	}
	else
	{
		LL[u][0][0] = LL[u][0][1] = RR[u][0][0] = RR[u][0][1] = ans[u][0][0] = ans[u][0][1] = 1;
	}
	for (int i = head[u]; ~i; i = edge[i].next) dfs(edge[i].to, u, deep + 1);
}
void process(int n)
{
	for (int j = 0; j < 16; j++)
		for (int i = 1; i <= n; i++)
		{
			if (dep[anc[i][j]]>(1 << j))
			{
				anc[i][j + 1] = anc[anc[i][j]][j];
				int A = anc[i][j];
				int len = (1 << j) + 1;

				if (LL[i][j][0] == len) LL[i][j + 1][0] = len - 1 + LL[A][j][0];
				else LL[i][j + 1][0] = LL[i][j][0];

				if (RR[A][j][0] == len) RR[i][j + 1][0] = len - 1 + RR[i][j][0];
				else RR[i][j + 1][0] = RR[A][j][0];

				if (LL[i][j][1] == len) LL[i][j + 1][1] = len - 1 + LL[A][j][1];
				else LL[i][j + 1][1] = LL[i][j][1];

				if (RR[A][j][1] == len) RR[i][j + 1][1] = len - 1 + RR[i][j][1];
				else RR[i][j + 1][1] = RR[A][j][1];

				ans[i][j + 1][0] = max(ans[i][j][0], ans[A][j][0]);
				ans[i][j + 1][0] = max(ans[i][j + 1][0], RR[i][j][0] + LL[A][j][0] - 1);

				ans[i][j + 1][1] = max(ans[i][j][1], ans[A][j][1]);
				ans[i][j + 1][1] = max(ans[i][j + 1][1], RR[i][j][1] + LL[A][j][1] - 1);
			}
		}
}
int lca(int u, int v)
{
	if (dep[u] < dep[v]) swap(u, v);
	for (int i = 16; i >= 0; i--) if (dep[u] - (1 << i) >= dep[v]) u = anc[u][i];
	if (u == v) return u;
	for (int i = 16; i >= 0; i--)
		if (anc[u][i] != anc[v][i])
		{
			u = anc[u][i];
			v = anc[v][i];
		}
	return anc[u][0];
}
int getans(int u, int v)
{
	int l = lca(u, v);
	int Ru = 1, ansu = 1;
	bool first = 1;
	for (int i = 16; i >= 0; i--)
		if (dep[u] - (1 << i) >= dep[l])
		{
			if (first)
			{
				Ru = RR[u][i][0];
				ansu = ans[u][i][0];
				first = 0;
				u = anc[u][i];
				continue;
			}
			ansu = max(ansu, ans[u][i][0]);
			ansu = max(ansu, Ru + LL[u][i][0] - 1);

			int len = (1 << i) + 1;

			if (len == RR[u][i][0]) Ru += RR[u][i][0] - 1;
			else Ru = RR[u][i][0];

			u = anc[u][i];
		}
	int Rv = 1, ansv = 1;
	first = 1;
	for (int i = 16; i >= 0; i--)
		if (dep[v] - (1 << i) >= dep[l])
		{
			if (first)
			{
				Rv = RR[v][i][1];
				ansv = ans[v][i][1];
				first = 0;
				v = anc[v][i];
				continue;
			}
			ansv = max(ansv, ans[v][i][1]);
			ansv = max(ansv, Rv + LL[v][i][1] - 1);

			int len = (1 << i) + 1;

			if (len == RR[v][i][1]) Rv += RR[v][i][1] - 1;
			else Rv = RR[v][i][1];

			v = anc[v][i];
		}
	int ans = max(ansu, ansv);
	ans = max(ans, Ru + Rv - 1);
	return ans;
}
int main()
{
	int T;
	int ks = 0;
	scanf("%d", &T);
	bool ok = 0;
	while (T--)
	{
		if (ok != 0)
		{
			puts("");
		}
		ok = 1;
		printf("Case #%d:\n", ++ks);
		int n;
		scanf("%d", &n);
		init();
		for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
		for (int i = 2; i <= n; i++)
		{
			int s;
			scanf("%d", &s);
			add(s, i);
		}
		dfs(1, -1, 1);
		process(n);
		int Q;
		scanf("%d", &Q);
		while (Q--)
		{
			int u, v;
			scanf("%d %d", &u, &v);
			printf("%d\n", getans(u, v));
		}
	}
	return 0;
}
这个方法,效率挺高的。
HDU 4718 The LCIS on the Tree 树上路径倍增_第1张图片

你可能感兴趣的:(DP,数据结构)