开车旅行

开车旅行

NOIp2012 day1 T3
题目大意
给出n个排成一行的城市,每个城市有一个不同的海拔。定义两个城市间的距离等于他们的高度差的绝对值,且绝对值相等的时候海拔低的距离近。有两个人轮流开车,从左往右走。A每次都选最近的,B每次都选次近的。旅行时有一个总路程x,如果两个人的总路程>x 或 有一个人无法按照自己的原则选择目的城市,旅行就终止。

  1.给出x0,求从哪一个城市出发,使得A走的路程/B走的路程最小。如果B走的路程=0,则比值视为无穷大。如果有多个城市满足要求,则输出海拔最高的那个城市。

  2.给出x和s(出发城市),求旅行终止是A的路程和B的路程。

输入描述 Input Description

第一行包含一个整数 N,表示城市的数目。

第二行有 N 个整数,每两个整数之间用一个空格隔开,依次表示城市 1 到城市 N 的海拔高度,即H1,H2,……,Hn,且每个Hi都是不同的。

第三行包含一个整数 X0。

第四行为一个整数 M,表示给定M组Si和 Xi。

接下来的M行,每行包含2个整数Si和Xi,表示从城市 Si出发,最多行驶Xi公里。

输出描述 Output Description

输出共M+1 行。

第一行包含一个整数S0,表示对于给定的X0,从编号为S0的城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小。

接下来的 M 行,每行包含 2 个整数,之间用一个空格隔开,依次表示在给定的 Si和Xi下小A行驶的里程总数和小B 行驶的里程总数。

样例输入       
4 
2 3 1 4 
3 
4 
1 3 
2 3 
3 3 
4 3
 
 输出
1
1 1
2 0
0 0
0 0
 
 
 
 

 


【数据范围】

对于 30%的数据,有 1≤N≤20,1≤M≤20;

对于 40%的数据,有 1≤N≤100,1≤M≤100;

对于 50%的数据,有 1≤N≤100,1≤M≤1,000;

对于 70%的数据,有 1≤N≤1,000,1≤M≤10,000;

对于100%的数据,有1≤N≤100,000,1≤M≤10,000,-1,000,000,000≤Hi≤1,000,000,000,

0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,数据保证 Hi互不相同。

解题方案

这一道题翻了题解才做出来,我在这里总结一下。

我们很容易可以发现,这可以用线段上的倍增来跳点。

我们可以把A和B合成一轮,这样我们用f[i][j]带表从第i个点跳2j轮所到达的点。

用da[i][j]代表第i个点跳2j轮所到达的点时A所走的路程。db[i][j]也是同样的意义。

所以计算路程的方法与倍增跳点的方法一样。关键是怎么出预处理,求出da[i][0],db[i][0],与f[i][0]

其实,网上有很多介绍预处理的实现方法,但主要的思想都是这样的:

我们可以从n到1依次将点(以其高度为关键字,并记下其编号)丢入一个有序队列。那么如果刚加进的点序号为i,最近点与次近点只可能在i-2,i-1,i+1,i+2中。我们就可以在这里确定数组sa[i],代表i号点的次近点,sb[i]代表i号点最近点。因为加入的顺序为n到1,所以肯定是合理的。

我们可以得到

f[i][0]=sb[sa[i]];

da[i][0]=abs(h[i]-h[sa[i]]);

db[i][0]=abs(h[sa[i]]-h[sb[sa[i]]]);

具体怎么实现,好像可以用离散化+链表,双向链表,平衡树.....但我认为最6的还是 STL 的 set
预处理好了,我们就可以按照倍增的方法,求出再限定距离能跳的点。注意,会有情况b跳不了单a跳的了,我们要特殊处理。
对于第一问,可以枚举。O(n log n)
第二问 就是询问 O(log n)
要开long long!
#include <cstdio>
#include <iostream>
#include <cmath>
#include <queue>
#include <algorithm>
#include <cstring>
#include <climits>
#include <set>  
#define MAXN 100000+10

#define Pair pair<long long,long long>
using namespace std;
long long n,a[MAXN],sa[MAXN],sb[MAXN],x0,m;
long long f[MAXN][20],da[MAXN][20],db[MAXN][20];
long long read(){
    long long in=0;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar())in=in*10+ch-'0';
    return in;
}
set <Pair> s;
set <Pair> :: iterator it1; 
set <Pair> :: iterator it2; 
void init()
{
    for(long long i=n;i>=1;i--)
    {
        long long num=0;
        pair<Pair,long long> tmp[5];memset(tmp,0,sizeof(tmp));
        s.insert(Pair(a[i],i));
        it2=it1=s.find(Pair(a[i],i));
        if(it1!=s.begin())
        {
            it1--;
            tmp[++num].first.first=abs((*it1).first-a[i]);
            tmp[num].first.second=(*it1).first;
            tmp[num].second=(*it1).second;
            if(it1!=s.begin()) 
            {
                it1--;
                tmp[++num].first.first=abs((*it1).first-a[i]);
                tmp[num].first.second=(*it1).first;
                tmp[num].second=(*it1).second;
            }
        }
        if(it2!=--s.end())
        {
            it2++;
            tmp[++num].first.first=abs((*it2).first-a[i]);
            tmp[num].first.second=(*it2).first;
            tmp[num].second=(*it2).second;
            if(it2!=--s.end())
            {
                it2++;
                tmp[++num].first.first=abs((*it2).first-a[i]);
                tmp[num].first.second=(*it2).first;
                tmp[num].second=(*it2).second;
            }
        }
        sort(tmp+1,tmp+num+1);
        sb[i]=tmp[1].second;
        if(num>=2) sa[i]=tmp[2].second;
        f[i][0]=sb[sa[i]];
        if(sa[i]) da[i][0]=abs(a[i]-a[sa[i]]);
        if(sb[sa[i]]&&sa[i]) db[i][0]=abs(a[sa[i]]-a[sb[sa[i]]]);
    }
    for(long long j=1;(1<<j)<=n;j++)
    {
        for(long long i=1;i<=n;i++)
        if(f[i][j-1])
        {
            da[i][j]=da[i][j-1]+da[f[i][j-1]][j-1];
            db[i][j]=db[i][j-1]+db[f[i][j-1]][j-1];
            f[i][j]=f[f[i][j-1]][j-1];
        }
    }
}
long long query(long long x,long long d,long long &xx,long long &yy)
{
    long long ansa=0,ansb=0,a=x;
    for(int j=(int)log2(n);j>=0;j--)
    if(f[a][j]&&ansa+ansb+da[a][j]+db[a][j]<=d)
    {
        ansa+=da[a][j];
        ansb+=db[a][j];
        a=f[a][j];
    }
    if(sa[a]&&ansa+ansb+da[a][0]<=d)
        ansa+=da[a][0];
    xx=ansa;
    yy=ansb;
}
int main()
{

    scanf("%lld",&n);
    for(long long i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    init();

    long long bp=0,xx=0,yy=0;
    
    scanf("%lld",&x0);
    for(long long i=1;i<=n;i++)
    {
        long long a=0,b=0;
        query(i,x0,a,b);
        if(b)
        {
            if(bp==0||xx*b>yy*a)
            {
                bp=i;
                xx=a;
                yy=b;
            }
        }
    }
    printf("%lld\n",bp);
    
    scanf("%lld",&m);
    for(long long i=1;i<=m;i++)
    {
        long long x=0,y=0,ansa=0,ansb=0;
        scanf("%lld%lld",&x,&y);
        query(x,y,ansa,ansb);
        printf("%lld %lld\n",ansa,ansb);
    }//*/
  return 0;
}

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值