BZOJ 2500

题目描述

小 T 与小 L 终于决定走在一起,他们不想浪费在一起的每一分每一秒,所以他们决定每天早上一同晨练来享受在一起的时光.
他们画出了晨练路线的草图,眼尖的小 T 发现可以用树来描绘这个草图.他们不愿枯燥的每天从同一个地方开始他们的锻炼,所以他们准备给起点标号后顺序地从每个起点开始(第一天从起点一开始,第二天从起点二开始……).而且他们给每条道路定上一个幸福的值.很显然他们每次出发都想走幸福值和最长的路线(即从起点到树上的某一点路径中最长的一条).他们不愿再经历之前的大起大落,所以决定连续几天的幸福值波动不能超过M(即一段连续的区间并且区间的最大值最小值之差不超过 M).他们想知道要是这样的话他们最多能连续锻炼多少天(hint:不一定从第一天一直开始连续锻炼)?
现在,他们把这个艰巨的任务交给你了!

输入

第一行包含两个整数 N, M(M<=10^9).
第二至第 N 行,每行两个数字 Fi , Di, 第 i 行表示第 i 个节点的父亲是 Fi,
且道路的幸福值是 Di.

输出

最长的连续锻炼天数

样例输入

3 2
1 1
1 3

样例输出

3

提示

50%的数据 N<=1000

80%的数据 N<=100000

100%的数据 N<=1000000

考试A掉了。。其实考试时候就用三个傻样例检查出来错误,再次心疼一发昨天被爆内存吓炸且不会数数的lc大佬,把1000000看成10^7。。

这题首先题目强制要求每天走幸福指数最高的链,那么dfs可以求出以u为根节点的子树的最长和次长链,如果每个节点都dfs一遍显然是可以求出来每个节点的ans的,但n^2的时间复杂度绝对是不能忍受的,所以我们再次进行dfs,那么对于u来说第一种情况是在子树中xjb搞,第二种是通过父亲再次走其他链,那么我们就需要一个参数,为从根节点到u最长链的长度,到每一个节点再用当前节点的最大或最小值来更新,额感觉说不太清,然后可以通过二分长度+维护两个单调队列来判断,如果存在一段区间使得最大-最小<=m 就为可行解。

code:

#define MAXN 1000005
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
int n,m,first[MAXN],e=1;
int w[MAXN][2],son[MAXN][2],ans[MAXN],f[MAXN][2];
typedef pair<int,int> pa;
 
 
struct edge{
    int u,v,w,next;
}a[MAXN];
 
void push(int u,int v,int w){
    a[e].u=u;
    a[e].v=v;
    a[e].w=w;
    a[e].next=first[u];
    first[u]=e++;
}
 
void dfs(int u){//第一个dfs,求次长和最长链,w[u][0]为最长链
    for(int i=first[u];i;i=a[i].next){
        dfs(a[i].v);
        if(w[a[i].v][0]+a[i].w>w[u][0]){
            w[u][1]=w[u][0];
            w[u][0]=a[i].w+w[a[i].v][0];
            son[u][1]=son[u][0];
            son[u][0]=a[i].v;
        }
        else if(w[a[i].v][0]+a[i].w>w[u][1]){
            son[u][1]=a[i].v;
            w[u][1]=w[a[i].v][0]+a[i].w;
        }
    }
}
 
void dp(int u,int fa,int val,int len_x){//len_x为上述的第二种情况
    if(u==1)ans[u]=w[u][0],f[u][0]=w[u][0],f[u][1]=w[u][1];//比较蠢的特判好像没用
    else{
        ans[u]=w[u][0];
        if(u!=son[fa][0])ans[u]=w[fa][0]+val;//情况1:在子树中
        if(ans[u]<len_x)ans[u]=len_x;//情况2:通过父亲走,用len_x更新
    }
    for(int i=first[u];i;i=a[i].next){
        if(a[i].v!=son[u][0])
            dp(a[i].v,u,a[i].w,max(len_x,w[u][0])+a[i].w);//如果不是最长连儿子,用最长链更新
        else dp(a[i].v,u,a[i].w,max(len_x,w[u][1])+a[i].w);
    }
}
 
int c[MAXN],b[MAXN],l,r;
int C[MAXN],B[MAXN],L,R;
 
bool ok(int len){
    l=1,r=0;L=1,R=0;
    for(int i=1;i<=len-1;i++){//先塞进去len-1个保证区间长度
        while(l<=r&&c[r]>=ans[i])r--;
        c[++r]=ans[i];
        b[r]=i;
        while(L<=R&&C[R]<=ans[i])R--;
        C[++R]=ans[i];
        B[R]=i;
    }
    for(int i=len;i<=n;i++){
        while(l<=r&&c[r]>=ans[i])r--;
        c[++r]=ans[i];
        b[r]=i;
        while(l<=r&&i-b[l]+1>len)l++;
        while(L<=R&&C[R]<=ans[i])R--;
        C[++R]=ans[i];
        B[R]=i;
        while(L<=R&&i-B[L]+1>len)L++;
        if(C[L]-c[l]<=m)return 1;
    }
    return 0;
}
 
int main(){
    //freopen("race.in","r",stdin);
    //freopen("race.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++){
        int f,d;
        scanf("%d%d",&f,&d);
        push(f,i,d);
    }
    dfs(1);dp(1,0,0,0);
    int l1=1,r1=n,Ans=0;
    while(l1<=r1){//二分
        int mid = l1+r1>>1;
        if(ok(mid))Ans=mid,l1=mid+1;
        else r1=mid-1;
    }
    printf("%d
",Ans);
}



原文地址:https://www.cnblogs.com/Cooook/p/7738521.html