1. 程式人生 > >NOIP2012 luoguP1081 開車旅行 題解

NOIP2012 luoguP1081 開車旅行 題解

不難 www getc ora 函數 char through ... 關鍵字

這道題是真滴火!(一晚上加一節信息課!)

先鏈接一下題目:luoguP1081 開車旅行

首先,這個預處理就極其變態,要與處理出每一個點往後走A會去哪裏,B會去哪裏。而且還必須O(nlogn)給它跑出來,反正這就要了我好久好久的時間,還沒想出來!那麽我們來慎重思考一下:

1.既然要讓我們這麽快的時間內把一個點東邊的高度最近和次近找出來,只能考慮先排序。那我們就先讓它以高度為關鍵字排一遍序,肯定還是要記錄一下原先的序號的。

2.模擬一下,如果我們要找第一個點(最西邊的點)的預處理,那不就是在排完序的數組中找一下它左邊兩個和它右邊兩個再比較一下找出最近和次近(這個應該不難想)。然後,如果再找第二個點的預處理,第一個點顯然有可能會幹擾到它,所以處理完第一個點之後我們考慮把它“刪”掉,這就可以用雙向鏈表來維護了(啦啦啦!別以為這個東西很NB,其實就是用一個l,r來記錄i點排序之後左邊和右邊的第一個東邊城市,啦啦啦!),實現還是很困難的//...冷笑...\\

對應Prepare(雙向鏈表部分在solve()裏面)函數!!!

3.預處理完我們就要維護x範圍內的a開的距離和b開的距離了。其實我是想了很久之後才知道怎麽用倍增的(當然是看的標簽之後才知道要用倍增的(我太菜了!!!))。不管了,直接倍增吧...

f[i][j]表示從i城市出發走2*2^j(就是a,b都走2^j)步到達的城市編號。 disA[i][j]表示從i城市出發走2*2^j(就是a,b都走2^j)步

a開了多遠。 disB[i][j]表示從i城市出發走2*2^j(就是a,b都走2^j)步b開了多遠。

對應Bz函數!!!最後倍增的查找對應getab函數!!!

4.一個小細節:以為我們是直接倍增跳a,b一起開(如上j),所以找a,b各走了多遠時最後還要特判一下a是否還可以在開一輪。

上代碼:

#include<iostream>
#include<cstdlib>
#include
<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #include<stack> #define lst long long #define rg register #define N 100050 #define Inf 2147483647 using namespace std; int n,m,X0,ans=n; struct CITY{ lst v; int num,l,r; }ljl[N]; int back[N],go[N],nA[N],nB[N]; int f[N][20]; lst disA[N][20],disB[N][20],a,b; double minn=2147483647; inline lst read() { rg lst s=0,m=1;char ch=getchar(); while(ch!=-&&(ch<0||ch>9))ch=getchar(); if(ch==-)m=-1,ch=getchar(); while(ch>=0&&ch<=9)s=(s<<3)+(s<<1)+ch-0,ch=getchar(); return s*m; } inline int cmp(rg const CITY &a,rg const CITY &b){return a.v<b.v;} inline int dis(rg int p,rg int q){return abs(ljl[p].v-ljl[q].v);} inline int pd(rg int x,rg int y,rg int now)//x小返回1,y小返回0 { if(!x)return 0;//x不存在 if(!y)return 1;//y不存在 return dis(x,now)<=dis(y,now);//返回小一些的 } inline void solve(rg int tt,rg int now) { rg int ll=ljl[now].l,rr=ljl[now].r; if(pd(ll,rr,now))//左邊離得近一些 if(pd(ljl[ll].l,rr,now))//左邊的左邊離得近一些 nB[tt]=back[ll],nA[tt]=back[ljl[ll].l]; else//右邊離得近一些 nB[tt]=back[ll],nA[tt]=back[rr]; else//右邊離得近一些 if(pd(ll,ljl[rr].r,now))//左邊離得近一些 nB[tt]=back[rr],nA[tt]=back[ll]; else//右邊的右邊離得近一些 nB[tt]=back[rr],nA[tt]=back[ljl[rr].r]; if(ll)ljl[ll].r=rr; if(rr)ljl[rr].l=ll; } inline void Prepare() { n=read(); for(rg int i=1;i<=n;++i)ljl[i].v=read(),ljl[i].num=i; sort(ljl+1,ljl+n+1,cmp);//以高度為關鍵字排序 for(rg int i=1;i<=n;++i)back[i]=ljl[i].num,go[back[i]]=i;//排完序之後的元素在原數組中的位置 for(rg int i=1;i<=n;++i)ljl[i].l=i-1,ljl[i].r=i+1; ljl[1].l=ljl[n].r=0; for(rg int i=1;i<=n;++i)solve(i,go[i]); } inline void Bz() { for(rg int i=1;i<=n;++i) { f[i][0]=nB[nA[i]]; disA[i][0]=dis(go[i],go[nA[i]]); disB[i][0]=dis(go[nA[i]],go[f[i][0]]); } for(rg int j=1;j<=19;++j) for(rg int i=1;i<=n;++i) { f[i][j]=f[f[i][j-1]][j-1]; disA[i][j]=disA[i][j-1]+disA[f[i][j-1]][j-1]; disB[i][j]=disB[i][j-1]+disB[f[i][j-1]][j-1]; } /* for(rg int i=1;i<=n;++i) for(rg int j=0;j<=3;++j) { printf(" f[%d][%d]=%d\n",i,j,f[i][j]); printf("disA[%d][%d]=%lld\n",i,j,disA[i][j]); printf("disB[%d][%d]=%lld\n",i,j,disB[i][j]); } */} inline void getab(rg int x,rg int now) { a=b=0; for(rg int i=19;i>=0;--i) if(f[now][i]&&(a+b+disA[now][i]+disB[now][i]<=x)) a+=disA[now][i],b+=disB[now][i],now=f[now][i]; if(nA[now]&&a+b+disA[now][0]<=x)a+=disA[now][0]; } int main() { Prepare();//預處理左右A,B的方案 // for(rg int i=1;i<=n;++i)printf("nA[%d]=%d nB[%d]=%d\n",i,nA[i],i,nB[i]); Bz();//處理倍增 X0=read(),m=read(); for(rg int i=1;i<=n;++i) { getab(X0,i); if(b&&1.0*a/b<minn) minn=1.0*a/b,ans=i; } printf("%d\n",ans); for(rg int i=1;i<=m;++i) { rg int s=read(),x=read(); getab(x,s); printf("%lld %lld\n",a,b); } return 0; }

ojbk!!!

NOIP2012 luoguP1081 開車旅行 題解