2018.11.03-dtoj-2910-獨木橋(bridge)
題目描述:
Alice和Bob是好朋友,這天他們帶了n個孩子一起走獨木橋。
獨木橋寬度很窄,不允許兩個或兩個以上的人並肩行走,所有人必須要前後一個接一個地通行。
Bob給所有的孩子蒙上了眼,並將他們放在橋中不同的位置上,孩子們初始的朝向不一定相同。Bob吹響哨聲後這些孩子們會按照初始的朝向開始移動,當兩個孩子移動到同一點時由於橋太窄他們無法穿過彼此,因此他們會同時轉身改變朝向,並接著朝新方向移動。
為了安全起見,在某個時刻Alice會詢問Bob某個孩子現在所處的位置。
更具體的,我們可以將問題抽象如下:
· 將獨木橋看作一個長度無限長的實數軸,將每個孩子看作數軸上的一個實數點。數軸從左到右座標不斷增大。
· 孩子的位置用相對於數軸原點的點的座標來表示。初始時n個點在n個互不相同的整點上。
· 每個點有一個初始朝向(從左向右或從右向左)。任何時刻所有的點都會以每秒1單位長度的速度勻速向所朝的方向移動。當某個時刻兩個點同時移動到了同一個位置上,它們會立即改變自己的朝向(從左向右變成從右向左,反之亦然),然後繼續移動。
·有q次詢問,每次詢問給定ki與ti,詢問在titi秒後,孩子ki目前的位置。
Bob無法同時關注這麼多的孩子,請你幫幫他。
輸入:
第一行一個整數n表示孩子數,孩子從0開始編號。
第二行n個整數pi,表示孩子們的初始位置。
第三行n個整數di,表示孩子們的初始朝向。di=0則初始向左,di=1則初始向右。
第四行一個整數q 表示詢問數。
接下來q行每行兩個整數ki,ti表示一個詢問,詢問在titi秒
後,孩子ki (按輸入順序)目前的位置。
輸出:
輸出q行每行一個整數表示答案。
資料範圍:
20%的資料:n,pi,ti≤10n,
另有20%的資料:di均相同
另有20%的資料:q≤10
另有15%的資料:ti≤100
另有15%的資料:n≤1000
1OO%的資料:1≤n,q≤2∗105 0≤ki<n 0≤pi,ti≤109 di∈0,1
演算法標籤:二分
思路:
這題的簡單版是螞蟻,妙的地方在於兩個孩子相撞繼續走,其實相當於像個孩子交換編號繼續不改方向的前行,所以其實最後有孩子的位置就是按原方向向前t的座標,至於每個孩子的位置在哪,由於每個孩子的排名始終不改變,即一個孩子的左右孩子的個數始終不變。所以我們可以把兩個方向的孩子放在兩個不同數組裡,每次二分一個答案座標,然後求出這個左邊的排名,效率是O(n*logn*log1e9)。
另一個nlogn的演算法相對難寫,似乎是用線段樹維護。
以下程式碼:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define re register #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=2e5+5,inf=2e9; int n,tot1,tot2,pos[N];struct node{int p,id,d;}t[N];int q1[N],q2[N]; il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;} bool cmp(node t1,node t2){return t1.p<t2.p;} int pd(LL x,int t){ int res1,res2; res1=upper_bound(q1+1,q1+1+tot1,x-t)-q1;res1--; res2=upper_bound(q2+1,q2+1+tot2,x+t)-q2;res2--; return res1+res2; } int main() { n=read();for(re int i=1;i<=n;i++){ t[i].p=read();t[i].id=i; } for(re int i=1;i<=n;i++)t[i].d=read(); sort(t+1,t+1+n,cmp); for(re int i=1;i<=n;i++)pos[t[i].id]=i; for(re int i=1;i<=n;i++){ if(t[i].d==0)q2[++tot2]=t[i].p; else q1[++tot1]=t[i].p; } int Q=read();while(Q--){ int k=pos[read()+1],tt=read(); int l=t[1].p-tt,r=t[n].p+tt,res;res=r; while(l<=r){ LL mid=(((LL)l+(LL)r)>>1ll); if(pd(mid,tt)>=k)res=mid,r=mid-1;else l=mid+1; } printf("%d\n",res); } return 0; }View Code