給你個點,每個點有兩種邊,和,給出所有點邊連向哪個點以及邊連向哪個點,(每個點往外只能連一條和一條)找到一條從到的路徑,使得:這條路徑上連續經過的路徑或者路徑最長長度最短。
看到最後一句話其實就知道這題正解了。
二分。
二分最長長度之後呢?
讓我們從最低檔分開始爬。
對於的範圍怎麼辦?把所有點拆成多個點,每層的點表示連續經過若干次或若干次到達,那麼二分最短長度,判斷連通性即可。
那麼呢?
上一個東西其實完全沒有使用到每個點往外連的邊:和只有一條!這個性質怎麼用?其實你只需要記錄每個點上一次轉移過來的地方是還是,然後反向走,那麼其實可以讓點和走的距離小於等於二分值的點都連上邊,用倍增優化建圖,然後就可以了。
(其實直接暴力我記得也能過的,這東西寫起來估計比正解還爽)。
正解:
考慮我們的操作,每次都是找到一個被走到的點,然後走步,反向,一定是可以保證路徑合法的,所以我們可以用一個隊列加並查集完成這個判定。
如果一個點沒有被訪問過,那麼這個點的父親等於他自身,否則就是不斷往某個方向走到的第一個沒有被訪問過的節點。順便記一下距離,判一下合法性即可。
#include <bits/stdc++.h>
#define regi register int
int n,a[2][200001],vis[2][200001],d[2][200001],fa[2][200001],l,r,mid;
std::queue<std::pair<int,int> >q;
inline int read(){
int r=0,w=0,c;
for(;!isdigit(c=getchar());r=c);
for(w=c^48;isdigit(c=getchar());w=w*10+(c^48));
return r^45?w:-w;
}
inline int find(int op,int x){
if(fa[op][x]==x)
return x;
regi Fa=find(op,fa[op][x]);
d[op][x]+=d[op][fa[op][x]];
return fa[op][x]=Fa;
}
bool check(int k){
for(regi i=0;i<2;++i)
for(regi j=1;j<=n;++j)
vis[i][j]=d[i][j]=0,fa[i][j]=j;
vis[0][1]=vis[1][1]=1;
q.push(std::make_pair(0,1));q.push(std::make_pair(1,1));
while(!q.empty()){
std::pair<int,int>now=q.front();
q.pop();
int op=now.first;
int x=now.second;
x=a[op][x];
for(regi y=x;;){
y=find(op,y);
if(vis[op^1][y])
break;
find(op,x);
if(d[op][x]>=k)
break;
vis[op^1][y]=1;
q.push(std::make_pair(op^1,y));
if(find(op,a[op][y])==y)
break;
fa[op][y]=a[op][y];
d[op][y]++;
}
}
return vis[0][n]|vis[1][n];
}
main(){
n=read();
if(n==1){
puts("0");
return 0;
}
for(regi i=1;i<=n;++i){
a[0][i]=read();
a[1][i]=read();
}
r=n+1;
while(l+1<r){
if(check(mid=l+r>>1))
r=mid;
else
l=mid;
}
printf("%d\n",r==n+1?-1:r);
return 0;
}