#单调队列,树形dp#ssl 2443 2570 bzoj 2500 幸福的道路

题目

一棵树,点的权值等于从该点走的最长的一条路,问最长的区间 [ l . . . r ] 最大点权与最小点权之差不超过 m


分析

首先先要求点权,也就是求树最长链,可以用树形dp解决,求出最长路+次长路,然后找区间必须得用 O ( n ) 的时间解决(理论上 O ( n l o g n ) 是会超时的),所以说可以用单调队列解决,然后就没有什么了


代码

#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);
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/81780272
今日推荐