您的位置:首页 > 其它

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

2014-03-23 01:19 489 查看
POJ 3162 Walking Race(树形DP+单调队列)
分析:首先这道题和HDU4123很像,不过这道题目数据规模很大,空间有限,不能用RMQ求解了,要用单调队列进行第二步处理。
HDU4123:/article/1517700.html
依然先树形DP,求出所有的d[i]距离,然后对于m找出最长的连续区间。维护两个单调队列qmin
和qmax
,
qmin和qmax中存的都是元素的下标.然后每次同时往qmin和qmax尾部加上d[j],使得qmin中的元素按照d值增摆放,
qmax中的元素按照d值减摆放.那么qmin中的第一个元素就是当前区间[i , j]内的最小值的下标,qmax中的第一个元素就是当前区间[i
, j]内的最大值下标.
初始时i为 1,j为1.然后当添加了某个元素后使得d[qmax[front2]]-d[qmin[front1]]
>M的话,说明我们刚添加的元素不符合要求,但是j-1这个元素还是符合要求的所以[i , j]内的ans=j-i.
现在问题是下一步我们应该找那些区间呢?从i+1作为左区间开始还是?因为我们刚添加的元素d[j]使得:
d[qmax[front2]]-d[qmin[front1]] >M,所以如果令i=min(qmax[front2],qmin[front1]),那么依然d[qmax[front2]]-d[qmin[front1]]
>M,不合法,且这时候得到的j-i更小没有意义.
所以i必须从min(qmax[front2],qmin[front1])+1开始,这样就有可能使得最大值减最小值合法,则可以更新ans的值.
上面的理论其实也是让左区间i保持不变,看j最大能到什么位置.更新ans,然后i到下一个合法的位置上去继续尽量找到最大j.
AC代码:3469ms
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN =1000000+5;
int tot;
int longest[MAXN];
int dist[MAXN][3];
int d[MAXN];
int head[MAXN];
int n,m;
int qmin[MAXN],qmax[MAXN];
struct edge
{
    int to;
    int next;
    int w;
}edges[MAXN*2];
void add_edge(int u,int v,int w)
{
    edges[tot].to=v;
    edges[tot].w=w;
    edges[tot].next=head[u];
    head[u]=tot++;
}
int dfs1(int u,int fa)
{
    if(dist[u][0]>=0)return dist[u][0];
    dist[u][0]=dist[u][1]=dist[u][2]=longest[u]=0;
    for(int e=head[u];e!=-1;e=edges[e].next)
    {
        int v= edges[e].to;
        if(v==fa)continue;

        if(dist[u][0]<dfs1(v,u)+edges[e].w)
        {
            dist[u][1] = dist[u][0];
            longest[u]=v;
            dist[u][0]=dfs1(v,u)+edges[e].w;
        }
        else if(dist[u][1]<dfs1(v,u)+edges[e].w)
            dist[u][1] = dfs1(v,u)+edges[e].w;
    }
    return dist[u][0];
}
void dfs2(int u,int fa)
{
    for(int e=head[u];e!=-1;e=edges[e].next)
    {
        int v=edges[e].to;
        if(v==fa)continue;

        if(v==longest[u])dist[v][2]=max(dist[u][2],dist[u][1])+edges[e].w;
        else dist[v][2]=max(dist[u][2],dist[u][0])+edges[e].w;
        dfs2(v,u);
    }
}
void solve()
{
    int ans=0;
    int i,j,front1,front2,rear1,rear2;
    front1=rear1=front2=rear2=0;//front1是队首元素的位置,rear1是对尾元素的下一个位置
    for(i=j=1;j<=n;j++)
    {
        while(front1<rear1 && d[qmin[rear1-1]]>=d[j])rear1--;
        qmin[rear1++]=j;//qmin中存的是d[j]的下标,且qmin中按值增存
        while(front2<rear2 && d[qmax[rear2-1]]<=d[j])rear2--;
        qmax[rear2++]=j;//qmax中存的是d[j]的下标,且qmax中按值减存
        //这样qmin中始终保持队首元素是区间[l,r]内d值最小的,qmax保证队首位d值最大的
        if(d[qmax[front2]]-d[qmin[front1]]>m)//突然不合法
        {
            ans = max(ans,j-i);
            i=min(qmin[front1],qmax[front2])+1;//此步的i必然使得最大值-最小值<=m,否则就是i已经>n了
            while(front1<rear1 && qmin[front1]<i)front1++;
            while(front2<rear2 && qmax[front2]<i)front2++;
        }
    }
    ans = max(ans,j-i);
    printf("%d\n",ans);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        if(n==0&&m==0)break;
        tot=0;
        memset(dist,-1,sizeof(dist));
        memset(head,-1,sizeof(head));
        for(int i=2;i<=n;i++)
        {
            int v,w;
            scanf("%d%d",&v,&w);
            add_edge(i,v,w);
            add_edge(v,i,w);
        }
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i=1;i<=n;i++)
            d[i] = max(dist[i][0] , dist[i][2]);
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: