【NOIP2012TG】 开车旅行 详解+代码

19 篇文章 0 订阅
10 篇文章 0 订阅

Description

小 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 行驶的路程总数的比 值都最小,则输出海拔最高的那个城市。

  2. 对M组任意给定的 X=Xi和出发城市 Si,小 A 开车行驶的路程总数以及小 B 行驶的路程 总数。

对于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互不相同。

Solution

显然,我们是可以预处理出来每个点第一近和第二近的点的

对于第一问,枚举出发点,然后走;
第二问就直接走就好了

预处理是O(N^2),第一问是O(N^2),第二问是O(MN)

时间复杂度是O(N^2+MN)

最多只能过70%

然而每走一次并不需要O(N)

我们可以用倍增的思想

把A开一次和B开一次看作是走一步

设next[i][j]表示i这个点,走2^j步到的点

pr[i][j][0]表示i这个点,走2^j步A所开的路程
pr[i][j][1]表示i这个点,走2^j步B所开的路程

然后再用类似倍增的思想走

走一次就变成log N 的了

复杂度就变成O(N^2+Mlog N)

再考虑优化预处理

很多网上的题解都是说要什么平衡树之类的

这里讲一种简单的

我们可以把所有的高度排一遍序,记录下原来的位置现在所对应的

用一个链表把这个排序后的序列存起来

从原来的1到N做,看他的左边第一个近还是右边第一个近。

这就是B的选择

A的情况:
假设选了左边第一个

那么就在左边第二个和右边第一个中找最近就是A的选择

原来选了右边同理

于是在O(N log N)复杂度内解决预处理

总复杂度O(N log N+Mlog N)

Code

我打的有点恶心,见谅

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note
{
    note *ls,*rs;
    int hi,wz;  
};
struct st
{
    long long x,y;
};
bool cmp(st x,st y)
{
    return x.x>y.x;
}
struct next
{
    long long x,y,z;
};
note *lfs;
int nt[2][100001],n,m;
long long xf;
note *fd[100001];
st hi[100001];
next ntz[20][100001];
int main()
{
    freopen("drive.in","r",stdin);
    freopen("drive.out","w",stdout);
    cin>>n;
    int i,j,k;
    fo(i,1,n) 
    {
        scanf("%lld",&hi[i].x);
        hi[i].y=i;
    }
    sort(hi+1,hi+n+1,cmp);
    lfs=new note;
    lfs->hi=hi[1].x;
    lfs->wz=hi[1].y;
    lfs->ls=NULL;
    lfs->rs=new note;
    note *now=lfs->rs;
    now->ls=lfs;
    fd[lfs->wz]=lfs;
    fo(i,2,n)
    {
        now->hi=hi[i].x;
        now->wz=hi[i].y;
        fd[now->wz]=now;
        now->rs=new note;
        now->rs->ls=now;
        now=now->rs;
    }
    now->ls->rs=NULL;
    fo(i,1,n-1)
    {
        long long a=100000000000,b=100000000000;
        int aw=0,bw=0;
        now=fd[i];
        bool bz=0;
        if (now->rs!=NULL) 
        {
            b=abs(now->hi-now->rs->hi);
            bw=now->rs->wz;
        }
        if (now->ls!=NULL) 
        {
            a=abs(now->hi-now->ls->hi);
            aw=now->ls->wz;
        }
        if(a<b) nt[1][i]=aw;
        if(b<a) 
        {
            nt[1][i]=bw;
            bz=1;
        }
        if (a==b)
        {
            if (now->ls->hi<now->rs->hi) nt[1][i]=aw;
            else 
            {
                nt[1][i]=bw;
                bz=1;
            }
        }
        if (i!=n-1)
        {
            a=10000000000000;
            b=10000000000000;
            now=fd[i];
            if (bz)
            {
                if (now->rs!=NULL&&now->rs->rs!=NULL) 
                {
                    b=abs(now->hi-now->rs->rs->hi);
                    bw=now->rs->rs->wz;
                }
                if (now->ls!=NULL) 
                {
                    a=abs(now->hi-now->ls->hi);
                    aw=now->ls->wz;
                }
                if(a<b) nt[0][i]=aw;
                if(b<a) nt[0][i]=bw;
                if (a==b)
                {
                    if (now->ls->hi<now->rs->rs->hi) nt[0][i]=aw;
                    else nt[0][i]=bw;
                }
            }
            else 
            {
                if (now->rs!=NULL) 
                {
                    b=abs(now->hi-now->rs->hi);
                    bw=now->rs->wz;
                }
                if (now->ls!=NULL&&now->ls->ls!=NULL) 
                {
                    a=abs(now->hi-now->ls->ls->hi);
                    aw=now->ls->ls->wz;
                }
                if(a<b) nt[0][i]=aw;
                if(b<a) nt[0][i]=bw;
                if (a==b)
                {
                    if (now->ls->ls->hi<now->rs->hi) nt[0][i]=aw;
                    else nt[0][i]=bw;
                }
            }
            if (now->rs!=NULL) 
            {
                now->rs->ls=now->ls;
            }
            if (now->ls!=NULL) 
            {
                now->ls->rs=now->rs;
            }
        }
    }
    fo(i,1,n-2)
    {
        if (nt[0][i]>=n) continue;
        ntz[0][i].x=nt[1][nt[0][i]];
        ntz[0][i].y=abs(fd[i]->hi-fd[nt[0][i]]->hi);
        ntz[0][i].z=abs(fd[nt[0][i]]->hi-fd[nt[1][nt[0][i]]]->hi);
    }
    fo(i,1,trunc(log2(n-1)))
    {
        fo(j,1,n-2)
        {
            if (i>trunc(log2(n-j))) break;
            ntz[i][j].x=ntz[i-1][ntz[i-1][j].x].x;
            ntz[i][j].y=ntz[i-1][j].y+ntz[i-1][ntz[i-1][j].x].y;
            ntz[i][j].z=ntz[i-1][j].z+ntz[i-1][ntz[i-1][j].x].z;
        }
    }
    cin>>xf;
    cin>>m;
    double ans=10000000000;
    int aw;
    fo(i,1,n-2)
    {
        int p=i;
        long long ns1=0,ns2=0;
        if (i==22911)
        {
            n=n;
            p=p;
        }
        j=0;
        while(j>=0)
        {   
            j=0;
            while(ntz[j][p].x!=0&&ns1+ns2+ntz[j][p].y+ntz[j][p].z<=xf)
            {   
                j++;
            }
            j--;
            if (j>=0)
            {
                ns1+=ntz[j][p].y;
                ns2+=ntz[j][p].z;

                p=ntz[j][p].x;
            }
        }
        if (nt[0][p]!=0&&ns1+ns2+abs(fd[p]->hi-fd[nt[0][p]]->hi)<=xf)
        {
            ns1+=abs(fd[p]->hi-fd[nt[0][p]]->hi);
            p=ntz[j][p].x;
        }
        if (ns1*1.0/ns2<ans)
        {
                ans=ns1*1.0/ns2;
                aw=i;
        }
    }
    cout<<aw<<endl;
    fo(i,1,m)
    {
        int st;
        scanf("%d%d",&st,&xf);
        int p=st;
        long long ns1=0,ns2=0;
        j=0;
        if (i==503)
        {
            p=p;
            n=n;
        }
        while(j>=0)
        {   
            j=0;
            while(ntz[j][p].x!=0&&ns1+ns2+ntz[j][p].y+ntz[j][p].z<=xf)
            {   
                j++;
            }
            j--;
            if (j>=0)
            {
                ns1+=ntz[j][p].y;
                ns2+=ntz[j][p].z;
                p=ntz[j][p].x;
            }
        }
        if (nt[0][p]!=0&&ns1+ns2+abs(fd[p]->hi-fd[nt[0][p]]->hi)<=xf)
        {
            ns1+=abs(fd[p]->hi-fd[nt[0][p]]->hi);
            p=ntz[j][p].x;
        }
        printf("%lld %lld\n",ns1,ns2);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值