POJ - 3162-Walking Race(树形dp+单调队列)

题意:给一棵n个结点边带权的树,记结点i到其他结点最远距离为d[i]。

问d数组构成的这个序列中满足其中最大值与最小值的差不超过m的连续子序列最长是多长。

树形dp来求树中的每个顶点到其他所有顶点距离的最大值。然后用单调队列求满足max-min<=m的连续子序列最长是多长。

//#include <bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>

using namespace std;
typedef long long ll;
const int maxn=1e6+100;
struct node{
    int x;
    ll w;
    node(int _x,ll _w)
    {
        x=_x;
        w=_w;
    }
};
vector<node>a[maxn];
int n;
ll m,dp[maxn],dpf[maxn];
void dfs(int u,int fa)
{
    for(int i=0;i<a[u].size();i++)
    {
        int v=a[u][i].x;
        if(v==fa) continue;
        dfs(v,u);
        dp[u]=max(dp[u],dp[v]+a[u][i].w);
    }
}
void DFS(int u,int fa)
{
    ll tmp1=0,tmp2=0;
    int x,y;
    for(int i=0;i<a[u].size();i++)
    {
        int v=a[u][i].x;
        if(v==fa) continue;
        if(tmp2<dp[v]+a[u][i].w)
        {
            tmp2=dp[v]+a[u][i].w;
            y=v;
            if(tmp1<tmp2)
            {
                swap(tmp1,tmp2);
                swap(x,y);
            }
        }
    }

    for(int i=0;i<a[u].size();i++)
    {
        int v=a[u][i].x;
        ll w=a[u][i].w;
        if(v==fa) continue;
        if(v==x)
            dpf[v]=max(dpf[u],tmp2)+w;
        else dpf[v]=max(dpf[u],tmp1)+w;
        DFS(v,u);
    }
}
ll c[maxn],q[maxn],p[maxn];
int main()
{
    ///cout << "Hello world!" << endl;
    ///freopen("in.txt","r",stdin);
    while(~scanf("%d%lld",&n,&m))
    {
        for(int i=1;i<=n;i++)
            a[i].clear();
        for(int i=2;i<=n;i++)
        {
            int j;ll w;
            scanf("%d%lld",&j,&w);
            a[j].push_back(node(i,w));
        }
        memset(dp,0,sizeof dp);
        memset(dpf,0,sizeof dpf);
        dfs(1,0);
        DFS(1,0);
        for(int i=1;i<=n;i++)
            c[i]=max(dp[i],dpf[i]);
        /*-------------------------------*/
        //序列为 c
        //单调队列求满足max-min<=m的连续子序列最长是多长
        int ans=0;
        int tail1=0,tail2=0,head1=1,head2=1,pre=1;
        for(int i=1;i<=n;i++)
        {
            while(tail1>=head1&&c[q[tail1]]>=c[i])
                tail1--;
            q[++tail1]=i;

            while(tail2>=head2&&c[p[tail2]]<=c[i])
                tail2--;
            p[++tail2]=i;
            if(c[p[head2]]-c[q[head1]]>m)
            {
                ans=max(i-pre,ans);
                if(q[head1]<p[head2])
                {
                    pre=q[head1]+1;
                    head1++;
                }
                else
                {
                    pre=p[head2]+1;
                    head2++;
                }
            }
        }
        ans=max(ans,n+1-pre);//这一句不要掉下

        /*-------------------------------*/
        printf("%d\n",ans);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/dllpXFire/article/details/81052561