hdu4123 树求每个节点的最长链+尺取法

题意:50000个点的树,每个点有一个人,每个人会跑到离自己初始点距离最远的点上,这个距离为distance[i]。给你500个查询,对于每个查询Q,找一段连续编号的人,比如[left,right],满足 max( distance[i] i∈[left,right] ) – min( distance[i] i∈[left,right] ) ≤ Q,并且使得length=right-left+1要最大,求这个最大的length

首先看到这个题目,就知道肯定得先求每个点的最长链,然后这是一个固定是算法(当初刚看的时候还不会这算法),就是三遍dfs或者bfs,第一二遍是找到树的最长链,第三遍是根据第二遍算出来的最长链的第二个节点再走一遍dfs,三遍每遍走的时候都更新每个点与根节点的最长距离。
处理完数组后,我们要解决询问,一开始,我的想法(没用的想法),是吧询问排序,然后从小到大解决,因为从小到大的询问的答案肯定是递增的(小的可以满足大的更可以满足),然后去二分那个答案的也就是(shangyige一开始 = 1)(shangyige,n)去二分,然后每次询问后我们都可以更新区间的左端点,但是这样过不去,因为最极端的数据是每次查询是nlogn,(每个询问的答案都是1的时候)很遗憾只能选择n的算法去处理询问。
然后n的算法就是尺取法。一开始我看见尺取法我还不理解为什么那样子是正确的,后来思考完才比较理解。
尺取法是去用左右端点的两个指针,固定L端点扩大R端点,就是每一个数都要当一遍区间左端点,因为他要求的是连续区间,举个例子,你L是3的时候,R扩大到了5就不能在扩大了,所以你以3为左端点的最大值就是5-3+1,然后L继续++;
L为4的时候,因为你L为3扩大到了,5如果你5满足不了,那么你就无法扩大R去更新ans,因为右端点一样,左端点少了上一个区间一个数。所以复杂度是o(n);当R移动到n时就答案出来了。
下面是代码,有问题可以留言,我会解答的。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdio>
using namespace std;
const long long max_ = 1000000 + 50;
int  xiann = 1, head[max_], n, m, ans[max_];
inline  int  read()
{
	int  s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
struct k {
	int to, next, value;
}xian[max_];
inline void add_(int a, int b, int c) {
	xian[xiann].to = b;
	xian[xiann].next = head[a];
	xian[xiann].value = c;
	head[a] = xiann;
	xiann++;
}
int length[max_], id, max_deep = -1;
inline void dfs(int now, int fa, int len) {
	length[now] = max(len, length[now]);
	if (len > max_deep) {
		id = now;
		max_deep = len;
	}
	for (register int i = head[now]; i; i = xian[i].next) {
		if (xian[i].to != fa) {
			dfs(xian[i].to, now, len + xian[i].value);
		}
	}
}
inline void solve() {
	max_deep = -1;
	dfs(1, -1, 0);
	max_deep = -1;
	dfs(id, -1, 0);
	dfs(id, -1, 0);
}
struct v {
	int weizhi, Q, ans;

}ask[max_];
int lg[max_];
inline bool cmp(const v & t1, const v &t2) {
	return t1.Q < t2.Q;
}
int stmin[max_][40], stmax[max_][40];
inline void zaost() {
	for (register int i = 1; i <= n; i++) {
		stmin[i][0] = stmax[i][0] = length[i];
	}
	int t = lg[n];//log2(n);
	for (register int j = 1; j <= t; j++) {
		for (register int i = 1; i + (1 << j) - 1 <= n; i++) {
			stmax[i][j] = max(stmax[i][j - 1], stmax[i + (1 << (j - 1))][j - 1]);
			stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << (j - 1))][j - 1]);
		}
	}
}

inline void lgg() {
	lg[0] = -1;
	for (register int i = 1; i <= 50000 + 2; i++) {
		lg[i] = lg[i >> 1] + 1;
	}
}
inline int askmax(int l, int r) {
	int k = lg[r - l + 1];
	return max(stmax[l][k], stmax[r - (1 << k) + 1][k]);
}
inline int askmin(int l, int r) {
	int k = lg[r - l + 1];
	return min(stmin[l][k], stmin[r - (1 << k) + 1][k]);
}
inline void qing() {
	xiann = 1;
	for (register int i = 1; i <= max(n, m); i++) {
		head[i] = length[i] = 0;
	}
}
int xunwen(int x, int y) {
	return (askmax(x, y) - askmin(x, y));
}
int main() {
	lgg();
	while (scanf_s("%d%d", &n, &m) != EOF && n && m) {
		for (register int i = 1; i <= n - 1; i++) {
			int a, b, c;
			scanf_s("%d%d%d", &a, &b, &c);
			add_(a, b, c);
			add_(b, a, c);
		}
		for (register int i = 1; i <= m; i++) {
			ask[i].Q = read();
			ask[i].weizhi = i;
		}
		solve();
		zaost();
		int shangyige = 1;
		for (register int i = 1; i <= m; i++) {
			int Q = ask[i].Q, L = 1, R = 1, ans = 0;
			while ((L <= n && R <= n) && L <= R) {
				while (R <= n && xunwen(L, R) <= Q) {
					ans = max(ans, R - L + 1);
					R++;
				}

				L++;
			}
			printf("%d\n", ans);
		}
		qing();
	}
	return 0;
}
发布了32 篇原创文章 · 获赞 3 · 访问量 679

猜你喜欢

转载自blog.csdn.net/qq_43804974/article/details/100849012