[IOI2018]高速公路收費——二分查找+bfs
阿新 • • 發佈:2019-01-31
ack turn 相同 define else mes 鏈接 -s 一個點
題目鏈接:
IOI2018highway
題目大意:給出一張$n$個點$m$條邊的無向圖,並給出一對未知的起點和終點,每條邊都有兩種邊權$A$和$B$(每條邊的$A$和$B$都分別相同),每次你可以設置每條邊的邊權並向交互庫詢問,交互庫會返回給你當前邊權下起點到終點的最短路,你需要在不多於$50$次的詢問後找出起點和終點。
我們設起點為$S$,終點為$T$。
首先需要一次詢問將邊權都設為$A$來知道$S$到$T$的最短路。然後我們可以用二分來找到一個處於$S$到$T$最短路上的點:每次將編號在$[0,mid]$的點的所有出邊設為$B$,其他的設為$A$。如果得到的最短路不變,那麽顯然編號在$[mid+1,n-1]$的點有處於$S$到$T$最短路上的點,反之編號在$[0,mid]$的點有處於$S$到$T$最短路上的點。我們設找到的這個點為$x$,那麽$S$與$T$中一定有一個點距離$x$較遠,我們設這個點為$S$。從$x$開始$bfs$,二分然後每次將$bfs$序的$[mid+1,n-1]$這些點的所有出邊設為$B$,其他邊設為$A$,這樣就能找到$S$,再從$S$開始$bfs$同樣二分$bfs$序找到$T$。這樣詢問次數是$3*log_{2}^{90000}+1=52$,可以得到$90$分。
既然第一步可以二分找到最短路上的一個點,那麽我們同樣也可以找到一條邊。每次將編號在$[0,mid]$的邊設為$B$其他邊設為$A$來找到最短路上的一條邊,對於這條邊的兩端點$(u,v)$顯然每個點到這兩個點的最短距離不同,我們按每個點到這兩個點的最短距離將離$u$更近的分為一部分,離$v$更近的分為另一部分,對於每部分還是二分$bfs$序來分別找到$S$和$T$,這樣最壞情況詢問次數為$1+log_{2}^{130000}+2*log_{2}^{45000}=50$,即可得到滿分。
#include"highway.h" #include<queue> #include<cstdio> #include<vector> #include<algorithm> #define ll long long using namespace std; vector<int>s[90010]; vector<int>to[90010]; int w[130010]; ll path; queue<int>q; int dis_u[90010]; int dis_v[90010]; int que_u[90010]; int que_v[90010]; int cnt_u; int cnt_v; int S,T; bool cmp_u(int x,int y) { return dis_u[x]<dis_u[y]; } bool cmp_v(int x,int y) { return dis_v[x]<dis_v[y]; } void find_pair(int n,vector<int> u,vector<int> v,int A,int B) { int num=u.size(); for(int i=0;i<num;i++) { s[u[i]].push_back(i); to[u[i]].push_back(v[i]); s[v[i]].push_back(i); to[v[i]].push_back(u[i]); } path=ask(vector<int>(w,w+num)); int l=0; int r=num-1; while(l<r) { int mid=(l+r)>>1; for(int i=0;i<num;i++) { w[i]=0; } for(int i=0;i<=mid;i++) { w[i]=1; } ll value=ask(vector<int>(w,w+num)); if(value==path) { l=mid+1; } else { r=mid; } } q.push(u[l]); dis_u[u[l]]=1; while(!q.empty()) { int now=q.front(); q.pop(); int len=to[now].size(); for(int i=0;i<len;i++) { if(!dis_u[to[now][i]]) { dis_u[to[now][i]]=dis_u[now]+1; q.push(to[now][i]); } } } q.push(v[l]); dis_v[v[l]]=1; while(!q.empty()) { int now=q.front(); q.pop(); int len=to[now].size(); for(int i=0;i<len;i++) { if(!dis_v[to[now][i]]) { dis_v[to[now][i]]=dis_v[now]+1; q.push(to[now][i]); } } } for(int i=0;i<n;i++) { if(dis_u[i]<dis_v[i]) { que_u[++cnt_u]=i; } else { que_v[++cnt_v]=i; } } sort(que_u+1,que_u+1+cnt_u,cmp_u); sort(que_v+1,que_v+1+cnt_v,cmp_v); l=1,r=cnt_u; while(l<r) { int mid=(l+r)>>1; for(int i=0;i<num;i++) { w[i]=0; } for(int i=mid+1;i<=cnt_u;i++) { int len=s[que_u[i]].size(); for(int j=0;j<len;j++) { w[s[que_u[i]][j]]=1; } } ll value=ask(vector<int>(w,w+num)); if(path==value) { r=mid; } else { l=mid+1; } } S=que_u[l]; l=1,r=cnt_v; while(l<r) { int mid=(l+r)>>1; for(int i=0;i<num;i++) { w[i]=0; } for(int i=mid+1;i<=cnt_v;i++) { int len=s[que_v[i]].size(); for(int j=0;j<len;j++) { w[s[que_v[i]][j]]=1; } } ll value=ask(vector<int>(w,w+num)); if(path==value) { r=mid; } else { l=mid+1; } } T=que_v[l]; answer(S,T); }
[IOI2018]高速公路收費——二分查找+bfs