NOIP2012 Day1 开车旅行

【问题描述】

。。。真心累,不想解释
有n个高度互不相同的城市从左到右在一条直线上分布。两个城市之间的距离恰好为两个城市高度的绝对值。现在小a和小b从某座城市出发,交替开车。小a只会选择从当前位置出发,向右距离第二短的城市,小b只会选择从当前位置出发,向右距离最短的城市,且他们开车的总距离不超过X。
在启程前,小a想知道两个问题:

1.对于一个给定的 X=X_0,从哪一个城市出发,小A 开车行驶的路程总数与小 B行驶的路程总数的比值最小(如果小 B 的行驶路程为 0 ,此时的比值可视为 无穷大,且两个无穷大视为相等)。如果从多个城市出发,小 A 开车行驶的路程总数与小B行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
2.给定x[i],s[i],小A开车总路程和小B开车总路程

【输入样例】

#1
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3

#2
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7

【输出样例】

#1
1
1 1
2 0
0 0
0 0

#2
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0

【数据范围】

100%的数据1≤N,M≤100,000,-10^9≤H≤10^9,0≤X≤10^9,1≤S≤N,0≤Xi≤10^9,保证Hi互不相同


【分析】

一眼看出给定s和x,路线一定是可计算和固定的
首先想到暴力,O(N^2)通过预处理每个点右边的最小值和次小值还可以混个70。(据说当年联赛浙江一等线都只有260.。。)

第一问中枚举每个点和第二问中m个询问使得复杂度变成了O((n+m)*k)
[k为计算一次路线的平均时间]
主要思想是简化预处理。

倍增算法,用A[i][j]表示从i出发交替开车2^j轮,A开过的路程;同理用B[i][j]表示从i出发交替开车2^j轮,B开过的路程;并用f[i][j]表示2^j轮后,到达的目的地。

先将各个城市从低到高排序,再记下每个城市排序后的编号。
把排序后的数列改写成一个双向链表,从原始数列的第一个城市开始枚举,由于他在最左边,则i-2,i-1,i+1,i+2中的两个数即为他的最小和次小点。删去这个节点重复上述步骤。

如此,可以用O(nlogn)的时间完成每个点最小值和次小值的查找(而且主要是排序耗时-.-||)
上面两种优化的结合,可以使在给定x和s情况下,logn的时间求出路程

下面附上代码一份:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
const int M=10010;
long long n,m,x0,large=0;
long long h[N];
long long s[N],x[N];
long long min1[N],min2[N];
struct node
{
    long long h,id;
}p[N];
long long af[N],pre[N],nxt[N];
long long f[N][20],A[N][20],B[N][20];
bool cmp(node t,node y)
{
    return t.h<y.h;
}
inline void prepare(int u,int i){//u记得是排序后的位置,i为排序前的位置
    if(pre[u])min1[i]=p[pre[u]].id;
    if((nxt[u]&&p[u].h-p[pre[u]].h>p[nxt[u]].h-p[u].h)||!pre[u])min1[i]=p[nxt[u]].id;
    if(min1[i] == p[pre[u]].id){
        if(pre[pre[u]])min2[i]=p[pre[pre[u]]].id;
        if((nxt[u]&&p[u].h-p[pre[pre[u]]].h>p[nxt[u]].h-p[u].h)||!pre[pre[u]])min2[i]=p[nxt[u]].id;
    }
    else {
        if(pre[u])min2[i]=p[pre[u]].id;
        if((nxt[nxt[u]]&&p[u].h-p[pre[u]].h>p[nxt[nxt[u]]].h-p[u].h)||!pre[u])min2[i]=p[nxt[nxt[u]]].id;
    }
    nxt[pre[u]]=nxt[u];
    pre[nxt[u]]=pre[u];
}
void Init()
{
//  freopen("drive.in","r",stdin);
//  freopen("drive.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&h[i]);
    scanf("%lld%lld",&x0,&m);
    for(int i=1;i<=m;i++)
        scanf("%lld%lld",&s[i],&x[i]);
    for(int i=1;i<=n;i++)
    {
        p[i].h=h[i];
        p[i].id=i;
    }
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        af[p[i].id]=i;
        pre[i]=i-1;
        nxt[i]=i+1;
    }
    pre[1]=0,nxt[n]=0;
    memset(min1,0,sizeof(min1));
    memset(min2,0,sizeof(min2));
    for(int i=1;i<=n;i++)
        prepare(af[i],i);
    //before,it's the stage of min1,min2;
    //after,it's the stage of A,B,f;
    for(int i=1;i<=n;i++)
    {
        if(min1[i])A[i][0]=abs(h[min2[i]]-h[i]);
        if(min2[i])B[i][0]=abs(h[min2[i]]-h[min1[min2[i]]]);
        f[i][0]=min1[min2[i]];
    }
    while(1<<large<=n) large++;
    large--;
    for(int i=1;i<=large;i++)
        for(int j=1;j<=n;j++)
        {
            f[j][i]=f[f[j][i-1]][i-1];
            A[j][i]=A[j][i-1]+A[f[j][i-1]][i-1];
            B[j][i]=B[j][i-1]+B[f[j][i-1]][i-1];
        }
    return ;
}
int main()
{
    Init();
    long long ansA(0),ansB(0),ans=-1;
    for(int i=1;i<=n;i++)
    {
        long long loc=i,rest=x0;
        long long resa(0),resb(0);
        for(int j=large;j>=0;j--)
            if(A[loc][j]+B[loc][j]<=rest&&f[loc][j]!=0)
            {
                rest=rest-A[loc][j]-B[loc][j];
                resa+=A[loc][j];
                resb+=B[loc][j];
                loc=f[loc][j];
            }
        if(A[loc][0]<=rest&&min2[loc]!=0)
        {
            resa+=A[loc][0];
            loc=min2[loc];
        }
        if(ans==-1)
            ansA=resa,ansB=resb,ans=i;
        else
        {
            if(resb==0)
            {
                if(ansB==0&&h[ans]<h[i])
                    ansA=resa,ansB=resb,ans=i;
            }
            else
            {
                if(ansB==0)
                    ansA=resa,ansB=resb,ans=i;
                else
                {
                    double tmp1,tmp2;
                    tmp1=ansA/(ansB*1.0),tmp2=resa/(resb*1.0);
                    if(tmp2<tmp1||tmp1==tmp2&&h[ans]<h[i])
                        ansA=resa,ansB=resb,ans=i;
                }
            }
        }
    }
    cout<<ans<<endl;
    for(int i=1;i<=m;i++)
    {
        long long loc=s[i],rest=x[i];
        long long resa(0),resb(0);
        for(int j=large;j>=0;j--)
            if(A[loc][j]+B[loc][j]<=rest&&f[loc][j]!=0)
            {
                rest=rest-A[loc][j]-B[loc][j];
                resa+=A[loc][j];
                resb+=B[loc][j];
                loc=f[loc][j];
            }
        if(A[loc][0]<=rest&&min2[loc]!=0)
        {
            resa+=A[loc][0];
            loc=min2[loc];
        }
        printf("%lld %lld\n",resa,resb);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/80931489