E - Walking Race POJ - 树形DP+单调队列

  • E - Walking Race

  •  POJ - 3162 
  • 题意:n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要在这n个距离里取连续的若干天,使得这些天里最大距离和最小距离的差小于M,问怎么取使得天数最多?
  • 思路分步解决,第一个问题典型的树上最远距离两遍DFS,dp更新即可。
  • 第二个问题固定的最值差,两个队列分别维护单调升与单调减序列。操作为先分别维护两个队列的单调性,然后 进行维护M的限制直到最大值与最小值差限制在M内时停止,长度更新自然是 用当前i为尾,用最靠前的不符合的元素的下一位为首。
  • #include<stdio.h>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define maxn 1000861
    #define ll long long
    int head[maxn],n,m,tot,x,y,temp;
    ll dp[maxn],low[maxn],QAQ[maxn],pre[maxn];
    int best[maxn],qmax[maxn],qmi[maxn];
    int he1,ta1,he2,ta2,ans;
    struct node
    {
        int v,to,w;
    } edge[maxn*2];
    void add(int x,int y,int z)
    {
        edge[++tot].v=y;
        edge[tot].w=z;
        edge[tot].to=head[x];
        head[x]=tot;
        edge[++tot].v=x;
        edge[tot].w=z;
        edge[tot].to=head[y];
        head[y]=tot;
    }
    int dfs(int root,int fa)
    {
        if(dp[root])
            return dp[root];
        best[root]=-1;
        dp[root]=low[root]=0;
        int bestson;
        for(int i=head[root]; i!=-1; i=edge[i].to)
        {
            int son=edge[i].v;
            if(son==fa)continue;
            int cost=edge[i].w;
            if(dfs(son,root)+cost>dp[root])
            {
                low[root]=dp[root];
                dp[root]=dp[son]+cost;
                bestson=son;
            }
            else if(dp[son]+cost>low[root])
                low[root]=dp[son]+cost;
        }
        best[root]=bestson;
        return dp[root];
    }
    void dfs1(int root,int fa)
    {
        for(int i=head[root]; i!=-1; i=edge[i].to)
        {
            int son=edge[i].v;
            if(son==fa)continue;
            int cost=edge[i].w;
            if(son==best[root])
                pre[son]=max(pre[root],low[root])+cost;
            else
                pre[son]=max(pre[root],dp[root])+cost;
            dfs1(son,root);
        }
    }
    int main()
    {
    
        while(~scanf("%d%d",&n,&m))
        {
            tot=ans=0;
            memset(head,-1,sizeof(head));
            for(int i=2; i<=n; i++)
            {
                scanf("%d%d",&x,&y);
                add(i,x,y);
            }
            dfs(1,-1);
            dfs1(1,-1);
            he1=he2=temp=1;
            ta1=ta2=0;
            ans=-1;
            for(int i=1; i<=n; i++)
            {
                QAQ[i]=max(dp[i],pre[i]);
                while(ta1>=he1&&QAQ[i]>QAQ[qmax[ta1]])ta1--;
                qmax[++ta1]=i;
                while(ta2>=he2&&QAQ[i]<QAQ[qmi[ta2]])ta2--;
                qmi[++ta2]=i;
                while(ta1>=he1&&ta2>=he2&&QAQ[qmax[he1]]-QAQ[qmi[he2]]>m)
                {
                    if(qmax[he1]>qmi[he2])
                    {
                        temp=qmi[he2]+1;
                        he2++;
                    }
                    else
                    {
                        temp=qmax[he1]+1;
                        he1++;
                    }
                }
                ans=max(ans,i-temp+1);
            }
            printf("%d\n",ans);
        }
        return 0;
    }
    

     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值