poj 3162 Walking Race

Walking Race
Time Limit: 10000MS Memory Limit: 131072K
Total Submissions: 2157 Accepted: 514
Case Time Limit: 3000MS

Description

flymouse’s sister wc is very capable at sports and her favorite event is walking race. Chasing after the championship in an important competition, she comes to a training center to attend a training course. The center has N check-points numbered 1 through N. Some pairs of check-points are directly connected by two-way paths. The check-points and the paths form exactly a tree-like structure. The course lasts N days. On the i-th day, wc picks check-point i as the starting point and chooses another check-point as the finishing point and walks along the only simple path between the two points for the day’s training. Her choice of finishing point will make it that the resulting path will be the longest among those of all possible choices.

After every day’s training, flymouse will do a physical examination from which data will obtained and analyzed to help wc’s future training be better instructed. In order to make the results reliable, flymouse is not using data all from N days for analysis. flymouse’s model for analysis requires data from a series of consecutive days during which the difference between the longest and the shortest distances wc walks cannot exceed a bound M. The longer the series is, the more accurate the results are. flymouse wants to know the number of days in such a longest series. Can you do the job for him?

Input

The input contains a single test case. The test case starts with a line containing the integers N (N ≤ 106) and M (M < 109). Then followN − 1 lines, each containing two integers fi and di (i = 1, 2, …, N − 1), meaning the check-points i + 1 and fi are connected by a path of length di.

Output

Output one line with only the desired number of days in the longest series.

Sample Input

3 2
1 1
1 3

Sample Output

3

Hint

Explanation for the sample:

There are three check-points. Two paths of lengths 1 and 3 connect check-points 2 and 3 to check-point 1. The three paths along with wc walks are 1-3, 2-1-3 and 3-1-2. And their lengths are 3, 4 and 4. Therefore data from all three days can be used for analysis.

Source

题目大意抽象出来后就是。

给你一棵树,然后标号为1~n,每条边都有一定的边权,那么从每个点出发都有一个最远距离dp[i];

先求出dp[]数组,然后再有500个询问,每个询问输入一个整数m,求num数组中最大值与最小值绝对值之差不超过m的最长的连续区间是多少

求树的最长路见hdu 2196.得到dp数组后再枚举区间。维护l和r指针。当区间最大值减最小值大于给定值时l++.(如果r++还是不满足条件)否则r++

我一共用三种方法写了。

第一版是RMQ写的。由于空间开不下,不能成功提交。但RMQ查询效率蛮高的。

#include <iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct node
{
    int next;
    int lenth;
    int weight;
    int to;
} edge[101000];
int n,m,cnt,head[51000],vis[51000],dp[51000];
int dp1[51000][20],dp2[51000][20];
int ma(int a,int b){ return a>b?a:b; }
int mi(int a,int b){ return a<b?a:b; }
void rmq_init()//RMQ初始化
{
    int i,j;
    for(i=1;i<=n;i++)
        {
            dp1[i][0]=dp[i];
            dp2[i][0]=dp[i];
        }
    for(j=1;(1<<j)<=n;j++)
        for(i=1;i+(1<<j)-1<=n;i++)
        {
            dp1[i][j]=ma(dp1[i][j-1],dp1[i+(1<<(j-1))][j-1]);
            dp2[i][j]=mi(dp2[i][j-1],dp2[i+(1<<(j-1))][j-1]);
        }
}
int rmq_max(int l,int r)//RMQ查询最大值
{
    int k=0;
    while((1<<(k+1))<=r-l+1)
        k++;
    return ma(dp1[l][k],dp1[r-(1<<k)+1][k]);
}
int rmq_min(int l,int r)//查询最小值
{
    int k=0;
    while((1<<(k+1))<=r-l+1)
        k++;
    return ma(dp2[l][k],dp2[r-(1<<k)+1][k]);
}
void adde(int fa,int son,int w)//加边
{
    edge[cnt].to=son;
    edge[cnt].next=head[fa];
    edge[cnt].weight=w;
    head[fa]=cnt++;
}
void dfs(int fa)
{
    vis[fa]=1;
    int p=head[fa];
    while(p!=-1)
    {
        if(vis[edge[p].to])
        {
            p=edge[p].next;
            continue;
        }
        dfs(edge[p].to);
        dp[fa]=ma(dp[fa],dp[edge[p].to]+edge[p].weight);
        edge[p].lenth=dp[edge[p].to]+edge[p].weight;
        p=edge[p].next;
    }
}
void solve(int fa,int son)//计算dp数组
{
    if(vis[son])
        return;
    int maxx=0,p;
    vis[son]=1;
    p=head[fa];
    while(p!=-1)
    {
        if(edge[p].to!=son)
           maxx=ma(maxx,edge[p].lenth);
        p=edge[p].next;
    }
    p=head[son];
    while(p!=-1)
    {
        if(edge[p].to==fa)
           {
               edge[p].lenth=maxx+edge[p].weight;
               break;
           }
        p=edge[p].next;
    }
    p=head[son];
    while(p!=-1)
    {
        dp[son]=ma(dp[son],edge[p].lenth);
        solve(son,edge[p].to);
        p=edge[p].next;
    }
}
int main()
{
    int i,f,l,r,w,p,mx,mn,ans;

    while(~scanf("%d%d",&n,&m))
    {
        memset(head,-1,sizeof head);
        memset(dp,0,sizeof dp);
        memset(vis,0,sizeof vis);
        memset(dp1,0,sizeof dp1);
        memset(dp2,0,sizeof dp2);
        cnt=0;
        ans=0;
        for(i=2;i<=n;i++)
        {
            scanf("%d%d",&f,&w);
            adde(f,i,w);
            adde(i,f,w);
        }
        dfs(1);
        memset(vis,0,sizeof vis);
        p=head[1];
        while(p!=-1)
        {
            solve(1,edge[p].to);
            p=edge[p].next;
        }
        rmq_init();
        l=r=1;
        while(r<=n)
        {
            mx=rmq_max(l,r);
            mn=rmq_min(l,r);
            if(mx-mn<=m)
            {
                ans=ma(ans,r-l+1);
                r++;
            }
            else
                l++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
第二版,线段树版。

#include <iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct node
{
    int next;
    int lenth;
    int weight;
    int to;
} edge[2000010];
struct node2//定义线段树区间结构
{
    int l,r,maxx,minn;//l,r分别代表左右端点。maxx,minn分别为最大值。最小值
                     //en标记[l,r]链子价值是否相同
} sbt[4000010];//4倍就合适了。
int n,m,cnt,head[1000010],vis[1000010],dp[1000010];
int ma(int a,int b){ return a>b?a:b; }
int mi(int a,int b){ return a<b?a:b; }
void btree(int l,int r,int k)//以[l,r]建立线段树.k为结点号下同
{
    int mid,ls,rs;//mid记录线段中点。ls,rs左右儿子
    sbt[k].l=l;
    sbt[k].r=r;
    if(l==r)//如果到了叶子结点。
    {
        sbt[k].maxx=sbt[k].minn=dp[l];
        return;
    }
    ls=k<<1;//计算左右儿子标号
    rs=k<<1|1;
    mid=(l+r)>>1;
    btree(l,mid,ls);//递归构造线段树
    btree(mid+1,r,rs);
    sbt[k].maxx=ma(sbt[ls].maxx,sbt[rs].maxx);
    sbt[k].minn=mi(sbt[ls].minn,sbt[rs].minn);
}
int qu_max(int l,int r,int k)//询问[l,r]
{
    int mid,ls,rs;
    if(sbt[k].l==l&&sbt[k].r==r)//如果被询问区间刚好匹配
       return sbt[k].maxx;
    ls=k<<1;
    rs=k<<1|1;
    mid=(sbt[k].l+sbt[k].r)>>1;
    if(l>mid)//递归询问
        return qu_max(l,r,rs);
    else if(r<=mid)
        return qu_max(l,r,ls);
    else
        return ma(qu_max(l,mid,ls),qu_max(mid+1,r,rs));
}
int qu_min(int l,int r,int k)//询问[l,r]间的最大值
{
    int mid,ls,rs;

    if(sbt[k].l==l&&sbt[k].r==r)//如果被询问区间刚好匹配
       return sbt[k].minn;
    ls=k<<1;
    rs=k<<1|1;
    mid=(sbt[k].l+sbt[k].r)>>1;
    if(l>mid)//递归询问
        return qu_min(l,r,rs);
    else if(r<=mid)
        return qu_min(l,r,ls);
    else
        return mi(qu_min(l,mid,ls),qu_min(mid+1,r,rs));
}
void adde(int fa,int son,int w)
{
    edge[cnt].to=son;
    edge[cnt].next=head[fa];
    edge[cnt].weight=w;
    head[fa]=cnt++;
}
void dfs(int fa)
{
    vis[fa]=1;
    int p=head[fa];
    while(p!=-1)
    {
        if(vis[edge[p].to])
        {
            p=edge[p].next;
            continue;
        }
        dfs(edge[p].to);
        dp[fa]=ma(dp[fa],dp[edge[p].to]+edge[p].weight);
        edge[p].lenth=dp[edge[p].to]+edge[p].weight;
        p=edge[p].next;
    }
}
void solve(int fa,int son)
{
    if(vis[son])
        return;
    int maxx=0,p;
    vis[son]=1;
    p=head[fa];
    while(p!=-1)
    {
        if(edge[p].to!=son)
           maxx=ma(maxx,edge[p].lenth);
        p=edge[p].next;
    }
    p=head[son];
    while(p!=-1)
    {
        if(edge[p].to==fa)
           {
               edge[p].lenth=maxx+edge[p].weight;
               break;
           }
        p=edge[p].next;
    }
    p=head[son];
    while(p!=-1)
    {
        dp[son]=ma(dp[son],edge[p].lenth);
        solve(son,edge[p].to);
        p=edge[p].next;
    }
}
int main()
{
    int i,f,l,r,w,p,mx,mn,ans;

    while(~scanf("%d%d",&n,&m))
    {
        memset(head,-1,sizeof head);
        memset(dp,0,sizeof dp);
        memset(vis,0,sizeof vis);
        cnt=0;
        ans=0;
        for(i=2;i<=n;i++)
        {
            scanf("%d%d",&f,&w);
            adde(f,i,w);
            adde(i,f,w);
        }
        getchar();
        dfs(1);
        memset(vis,0,sizeof vis);
        p=head[1];
        while(p!=-1)
        {
            solve(1,edge[p].to);
            p=edge[p].next;
        }
        btree(1,n,1);
        l=r=1;
        while(r<=n)
        {
            mx=qu_max(l,r,1);
            mn=qu_min(l,r,1);
            if(mx-mn<=m)
            {
                ans=ma(ans,r-l+1);
                r++;
            }
            else
                l++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

终极版。也是最快的版本。单调队列版。

#include <iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct node
{
    int next;
    int lenth;
    int weight;
    int to;
} edge[2000010];
int n,m,cnt,head[1000010],vis[1000010],dp[1000010];
int q_max[1000010],q_min[1000010],hmax,hmin,tmax,tmin;
int ma(int a,int b){ return a>b?a:b; }
int mi(int a,int b){ return a<b?a:b; }

void adde(int fa,int son,int w)
{
    edge[cnt].to=son;
    edge[cnt].next=head[fa];
    edge[cnt].weight=w;
    head[fa]=cnt++;
}
void dfs(int fa)
{
    vis[fa]=1;
    int p=head[fa];
    while(p!=-1)
    {
        if(vis[edge[p].to])
        {
            p=edge[p].next;
            continue;
        }
        dfs(edge[p].to);
        dp[fa]=ma(dp[fa],dp[edge[p].to]+edge[p].weight);
        edge[p].lenth=dp[edge[p].to]+edge[p].weight;
        p=edge[p].next;
    }
}
void solve(int fa,int son)
{
    if(vis[son])
        return;
    int maxx=0,p;
    vis[son]=1;
    p=head[fa];
    while(p!=-1)
    {
        if(edge[p].to!=son)
           maxx=ma(maxx,edge[p].lenth);
        p=edge[p].next;
    }
    p=head[son];
    while(p!=-1)
    {
        if(edge[p].to==fa)
           {
               edge[p].lenth=maxx+edge[p].weight;
               break;
           }
        p=edge[p].next;
    }
    p=head[son];
    while(p!=-1)
    {
        dp[son]=ma(dp[son],edge[p].lenth);
        solve(son,edge[p].to);
        p=edge[p].next;
    }
}
int main()
{
    int i,f,l,r,w,p,mx,mn,ans;

    while(~scanf("%d%d",&n,&m))
    {
        memset(head,-1,sizeof head);
        memset(dp,0,sizeof dp);
        memset(vis,0,sizeof vis);
        cnt=0;
        ans=0;
        for(i=2;i<=n;i++)
        {
            scanf("%d%d",&f,&w);
            adde(f,i,w);
            adde(i,f,w);
        }
        dfs(1);
        memset(vis,0,sizeof vis);
        p=head[1];
        while(p!=-1)
        {
            solve(1,edge[p].to);
            p=edge[p].next;
        }
        l=r=1;
        hmax=hmin=0;//初始化两个单调队列。一个维护到r时区间最大值一个维护最小值
        tmax=tmin=0;
        q_max[hmax]=1;//初始把第一个元素入队
        q_min[hmin]=1;
        while(r<=n)
        {
            while(q_max[hmax]<l)//hmax最大为r.因为刚加入r所以r必在队尾。而l小于r
                hmax++;
            mx=dp[q_max[hmax]];
            while(q_min[hmin]<l)//如果队首元素超过范围则出队
                hmin++;
            mn=dp[q_min[hmin]];
            if(mx-mn<=m)
            {
                ans=ma(ans,r-l+1);
                r++;
                while(dp[r]>dp[q_max[tmax]]&&tmax>=hmax)
                   tmax--;
                q_max[++tmax]=r;//把新元素入队
                while(dp[r]<dp[q_min[tmin]]&&tmin>=hmin)
                   tmin--;
                q_min[++tmin]=r;
            }
            else
                l++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值