NOIP 2012 开车旅行

题目描述
小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j] = |Hi− Hj|。旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。他们计划选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。小 A 和小 B的驾驶风格不同,小 B 总是沿着前进方向选择一个最近的城市作为目的地,而小 A 总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。
在启程之前,小 A 想知道两个问题:
1.对于一个给定的 X=X0,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小(如果小 B 的行驶路程为 0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。对于任意给定的 X=Xi和出发城市 Si,小 A 开车行驶的路程总数以及小 B 行驶的路程总数。


【题目分析】
题目中给的天数是很多的,所以一般就会想到用倍增的方法来解决。我们先预处理出在每一个点,行走2^i个轮流(2^0,需要进行特判)。然后利用预处理出的数据进行统计,可以在logn的时间内完成统计。


【代码】

#include <cstdio>
#include <iostream>
#include <cstring>
#include <set>
#include <algorithm>
#include <map>
#include <cmath> 
#define inf 50000000000LL
using namespace std;
set <long long> q;
map <long long,int> mp;
int n,x0,m,h[100001],fa[100001],fb[100001],to[100001][17];
long long a[100001],b[100001],va[100001][17],vb[100001][17];
struct data{long long h,key;}t[5];
inline bool operator <(data a,data b)
{return a.key<b.key||(a.key==b.key&&a.h<b.h);}
void pre()
{
    for(int i=n;i;i--)
    {
        q.insert(h[i]);
        t[1].h=*--q.lower_bound(h[i]),t[2].h=*q.upper_bound(h[i]);
        if(t[1].h!=-inf)t[3].h=*--q.lower_bound(t[1].h);
        else t[3].h=-inf;
        if(t[2].h!=inf)t[4].h=*q.upper_bound(t[2].h);
        else t[4].h=inf;
        for(int k=1;k<=4;k++)
            t[k].key=abs(t[k].h-h[i]);
        sort(t+1,t+5);
        a[i]=t[2].key;fa[i]=mp[t[2].h];
        b[i]=t[1].key;fb[i]=mp[t[1].h];
        for(int j=0;j<=16;j++)
            if(j==0)
            {
                if(fa[i]){va[i][0]=a[i];to[i][0]=fa[i];}
            }
            else if(j==1)
            {
                if(fb[fa[i]]){va[i][1]=a[i];vb[i][1]=b[fa[i]];to[i][1]=fb[fa[i]];}
            }
            else if(to[to[i][j-1]][j-1])
            {
                va[i][j]=va[i][j-1]+va[to[i][j-1]][j-1];
                vb[i][j]=vb[i][j-1]+vb[to[i][j-1]][j-1];
                to[i][j]=to[to[i][j-1]][j-1];
            }
            else break;
    }
}
double call(int x,int val)
{
    int t1=0,t2=0;
    for (int i=16;i>=0;--i)
        if (to[x][i]&&t1+va[x][i]+t2+vb[x][i]<=val)
            {t1+=va[x][i];t2+=vb[x][i];x=to[x][i];}
    if (t2==0) return inf;
    return (double)t1/(double)t2;
}
void cal2(int x,int val)
{
    int t1=0,t2=0;
    for (int i=16;i>=0;--i)
        if (to[x][i]&&t1+va[x][i]+t2+vb[x][i]<=val)
            {t1+=va[x][i];t2+=vb[x][i];x=to[x][i];}
    printf("%d %d\n",t1,t2);
}
void solve1()
{
    double mn=1e60;int ans;
    scanf("%d",&x0);
    for (int i=1;i<=n;++i)
    {
        double t=call(i,x0);
        if (t<mn||(t==mn&&h[i]>h[ans])) {mn=t;ans=i;}
    }
    printf("%d\n",ans);
}
void solve2()
{
    scanf("%d",&m);
    for (int i=1;i<=m;++i)
    {
        int s,x;scanf("%d%d",&s,&x);
        cal2(s,x);
    }
}
int main()
{
    scanf("%d",&n);
    q.insert(-inf),q.insert(inf);
    for (int i=1;i<=n;++i)
        {scanf("%d",&h[i]);mp[h[i]]=i;}
    pre();
    solve1();
    solve2();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值