题意:wc建立了一个体育中心,共有N个检查点,这些检查点形成了一个树的形状。第i天,为了锻炼体质,wc从编号为i的节点出发,走到距离第i个点的最远的点。wc有一个仪器可以分析他连续几天的运动情况,但是被分析的这几天的距离的最大值和最小值的差不能超过M。请问这个仪器最长能够分析几天的结果。
思路:
第一问就是求出树上每个点的最长路径,这个就可以通过DFS得到。
第二问是求出最长的连续序列,使序列的最大值和最小值的差不大于M。
刚开始用map做的,
Θ(nlogn)
超时了,所以应该用线性的单调队列做。用两个单调队列,分别保留当前的最大值和最小值,这样就是
Θ(n)
的时间复杂度了。
代码如下:
#include <cstdio>
#include <cstring>
#include <deque>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 1000010;
struct edge{
int to,w;
edge(){}
edge(int t, int ww):to(t),w(ww){}
} edges[MAX<<1];
int N,M;
int nxt[MAX<<1],head[MAX],tot;
int dp[MAX],tmp[MAX];
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void addedge(int u, int v, int w)
{
edges[tot] = edge(v,w);
nxt[tot] = head[u];
head[u] = tot++;
}
void dfs(int u, int p, int d,int * dis)
{
dis[u] = d;
for(int i = head[u]; ~i; i = nxt[i]){
edge & e = edges[i];
if(e.to == p) continue;
dfs(e.to,u,d+e.w,dis);
}
}
int main(void)
{
//freopen("input.txt","r",stdin);
scanf("%d%d",&N,&M);
init();
for(int i = 2; i <= N; ++i){
int v, w;
scanf("%d%d",&v,&w);
addedge(i,v,w);
addedge(v,i,w);
}
dfs(1,0,0,dp);
int p1 = 1,l1 = 0, p2 = 1, l2 = 0;
for(int i = 1; i <= N; ++i){
if(dp[i] > l1){
p1 = i;
l1 = dp[i];
}
}
dfs(p1,0,0,dp);
for(int i = 1; i <= N; ++i){
if(dp[i] > l2){
p2 = i;
l2 = dp[i];
}
}
dfs(p2,0,0,tmp);
for(int i = 1; i <= N; ++i)
dp[i] = max(dp[i],tmp[i]);
int ans = 0;
deque<int> d1,d2;
for(int i = 1,j = 1; i <= N; ++i){
while(!d1.empty() && dp[d1.back()] <= dp[i]) d1.pop_back();
while(!d2.empty() && dp[d2.back()] >= dp[i]) d2.pop_back();
d1.push_back(i);
d2.push_back(i);
if(dp[d1.front()] - dp[d2.front()] > M){
j = min(d1.front(),d2.front()) + 1;
while(!d1.empty() && d1.front() < j) d1.pop_front();
while(!d2.empty() && d2.front() < j) d2.pop_front();
}
ans = max(ans,i-j+1);
}
printf("%d\n",ans);
return 0;
}