题意: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;
}