题目
一棵树,点的权值等于从该点走的最长的一条路,问最长的区间 [ l . . . r ] [l...r] [l...r]最大点权与最小点权之差不超过 m m m
分析
首先先要求点权,也就是求树最长链,可以用树形dp解决,求出最长路+次长路,然后找区间必须得用 O ( n ) O(n) O(n)的时间解决(理论上 O ( n l o g n ) O(nlogn) O(nlogn)是会超时的),所以说可以用单调队列解决,然后就没有什么了
代码
#include <cstdio>
#define N 1000001
struct node{int y,w,next;}e[N];
struct rec{int w,num;}d1[N],d2[N];
int ls[N],n,m,q1[N],q2[N];
int in(){
int ans=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans;
}
int max(int a,int b){return (a>b)?a:b;}
void dp(int x,int fa){
for (register int i=ls[x];i;i=e[i].next){
dp(e[i].y,x);
if (d1[e[i].y].w+e[i].w>d1[x].w)//次长路=当前最长路,当前最长路=更长路
d2[x]=d1[x],d1[x]=(rec){d1[e[i].y].w+e[i].w,e[i].y};
else if (d1[e[i].y].w+e[i].w>d2[x].w)
d2[x]=(rec){d1[e[i].y].w+e[i].w,e[i].y};//次长路=更长路
}
}
void dfs(int x,int fa,int w){
int len=(d1[fa].num==x)?d2[fa].w+w:d1[fa].w+w;//最长路+次长路
if (len>d1[x].w) d2[x]=d1[x],d1[x]=(rec){len,fa};//the same
else if (len>d2[x].w) d2[x]=(rec){len,fa};
for (int i=ls[x];i;i=e[i].next)
dfs(e[i].y,x,e[i].w);
}
int main(){
n=in(); m=in();
int ans=1,head1=1,tail1=0,head2=1,tail2=0;
for (register int i=2;i<=n;i++){
int x=in();
e[i-1]=(node){i,in(),ls[x]},ls[x]=i-1;
}
dp(1,0); dfs(1,0,0); int num=1;
for (register int i=1;i<=n;i++){
while (head1<=tail1&&d1[q1[tail1]].w>=d1[i].w) tail1--; q1[++tail1]=i; //更优的答案
while (head2<=tail2&&d1[q2[tail2]].w<=d1[i].w) tail2--; q2[++tail2]=i;//the same
while (d1[q2[head2]].w>d1[q1[head1]].w+m){//在区间外
if (q1[head1]<q2[head2]) num=q1[head1++]+1;//num表示当前满足区间的在哪里
else num=q2[head2++]+1;
}
ans=max(ans,i-num+1);//求答案
}
return !printf("%d",ans);
}