1. 程式人生 > >NOIP2012day1 開車旅行(倍增跳躍)

NOIP2012day1 開車旅行(倍增跳躍)

題意

https://www.luogu.org/problemnew/show/P1081

思路

首先 50 分的暴力跟著晦澀的題意強行打了一遍,然後對題意有了更深刻的理解,發現可以預處理小 A 和小

B 從某個點出發下一個走的點 ( n 2 ) ,就不用每次
f o r
一遍找了,這樣就成了 70 分。
不管是第一近還是第二近的點,都是因為海拔相近,所以用連結串列或set維護一個海拔有序的序列,然後按一定順序刪除/加入,在
2 , 1 , + 1 , + 2
的位置查詢即可。
知道了走一步的位置,那麼走 2 k 步也能應聲而出了,預處理倍增陣列,然後倒置迴圈倍增跳躍即可。

程式碼

#include<bits/stdc++.h>
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 100003
typedef long long LL;
using namespace std;
int h[N],L[N],R[N],idx[N],nxt[N][2],dis[N][2],g[N][23];
LL f[N][23][2];
int n,m;
struct node
{
    int x,val;
    bool operator <(const node &_)const{return val<_.val;}
}a[N];

void init()
{
    memset(dis,0x7f,sizeof(dis));
    FOR(i,1,n)a[i]=(node){i,h[i]};
    sort(a+1,a+1+n);
    FOR(i,1,n)idx[a[i].x]=i;
    FOR(i,1,n)L[i]=i-1,R[i]=i+1;
    L[1]=R[n]=0;
    FOR(i,1,n)
    {
        int k=idx[i];
        if(L[k])
        {
            k=L[k];
            nxt[i][0]=a[k].x,dis[i][0]=h[i]-a[k].val;
            if(L[k])
            {
                k=L[k];
                nxt[i][1]=a[k].x,dis[i][1]=h[i]-a[k].val;
            }
        }
        k=idx[i];
        if(R[k])
        {
            k=R[k];
            if(a[k].val-h[i]<dis[i][0])
            {
                nxt[i][1]=nxt[i][0],dis[i][1]=dis[i][0];
                nxt[i][0]=a[k].x,dis[i][0]=a[k].val-h[i];
            }
            else if(a[k].val-h[i]<dis[i][1])
                nxt[i][1]=a[k].x,dis[i][1]=a[k].val-h[i];
            if(R[k])
            {
                k=R[k];
                if(a[k].val-h[i]<dis[i][1])
                    nxt[i][1]=a[k].x,dis[i][1]=a[k].val-h[i];
            }
        }
        k=idx[i];
        L[R[k]]=L[k],R[L[k]]=R[k];
    }
    FOR(i,1,n)
    {
        g[i][0]=nxt[nxt[i][1]][0];
        f[i][0][1]=dis[i][1];
        f[i][0][0]=dis[nxt[i][1]][0];
    }
    FOR(j,1,20)
        FOR(i,1,n)
        {
            g[i][j]=g[g[i][j-1]][j-1];
            f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
            f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
        }
}
void Travel(int s,int X,LL &A,LL &B)
{
    A=B=0;
    DOR(i,20,0)
        if(g[s][i]&&A+B+f[s][i][1]+f[s][i][0]<=X)
        {
            A+=f[s][i][1];
            B+=f[s][i][0];
            s=g[s][i];
        }
    if(nxt[s][1]&&A+B+dis[s][1]<=X)
        A+=dis[s][1];
}
bool Smaller(LL a,LL b,LL c,LL d)
{
    if(b==0)return false;
    else if(d==0)return true;
    else return 1.0*a/b<1.0*c/d;
}
bool Equal(LL a,LL b,LL c,LL d)
{
    if(b==0&&d==0)return 1;
    else if(b*d==0)return 0;
    else return fabs(1.0*a/b-1.0*c/d)<=1e-7;
}

int main()
{
    scanf("%d",&n);
    FOR(i,1,n)scanf("%d",&h[i]);
    init();
    int X0,ansH=-2e9,ansS;
    LL ansA=2e9,ansB=0;
    scanf("%d",&X0);
    FOR(i,1,n)
    {
        LL resA,resB;
        Travel(i,X0,resA,resB);
        if(Smaller(resA,resB,ansA,ansB)||Equal(resA,resB,ansA,ansB)&&h[i]>ansH)
        {
            ansS=i;
            ansH=h[i];
            ansA=resA,ansB=resB;
        }
    }
    printf("%d\n",ansS);
    scanf("%d",&m);
    while(m--)
    {
        int s,t;LL A,B;
        scanf("%d%d",&s,&t);
        Travel(s,t,A,B);
        printf("%lld %lld\n",A,B);
    }
    return 0;
}