[loj3835]千岛
独木舟可以看作将边定向,并在每次经过后反向,要求最终每条边方向不变
在此基础上,考虑以下两种情况:
-
对于出度为\(0\)的点,到达其后仅能原路返回,不妨删除
-
若起点出度为\(1\),显然第一步移动唯一,移动后起点出度变为\(0\),仅能从该边返回(并结束)
换言之,可以将该点删除并将起点移动到出边终点
重复上述过程,若起点被删除则无解,否则对所有点仅保留一条出边
此时构成基环内向树,可以从起点到环绕一圈后返回,但环上的边方向改变
利用起点出度\(\ge 2\),交替选择两条出边各两次即可,路径长度至多为\(8n\)
一个特殊情况时是两条出边相互影响(参考1-02.in),此时仅需做\(3\)轮即可
时间复杂度为\(O(n+m\log m)\)
#include<bits/stdc++.h>
#include "islands.h"
using namespace std;
typedef vector<int> vi;
const int N=100005;
int n,m,st,flag,vis[N];
pair<int,int>to[N];
vi v,v0,ans,e[N];
queue<int>q;
set<pair<int,int> >S[N];
bool init(){
for(int i=1;i<=n;i++)
if (S[i].empty())q.push(i);
while ((!q.empty())||(S[st].size()<2)){
int k;
if (!q.empty()){
k=q.front(),q.pop();
if (k==st)return 0;
}
else{
ans.push_back((*S[st].begin()).second);
k=st,st=(*S[st].begin()).first;
}
vis[k]=1;
for(int i:e[k])
if (!vis[i]){
S[i].erase(S[i].lower_bound(make_pair(k,0)));
if (S[i].empty())q.push(i);
}
}
return 1;
}
void calc(){
for(int i=1;i<=n;i++)vis[i]=0;
int k=st;v.clear();
while (!vis[k]){
ans.push_back(to[k].second);
vis[k]=1,k=to[k].first;
}
if (k!=st)flag=1;
v.clear();
for(int i=st;i!=k;i=to[i].first)v.push_back(to[i].second);
reverse(v.begin(),v.end());
for(int i:v)ans.push_back(i);
pair<int,int>lst=to[k];
for(int i=k;;){
swap(i,lst.first),swap(lst,to[i]);
if (i==k)break;
}
}
variant<bool,vi> find_journey(int _n,int _m,vi _u,vi _v){
n=_n,m=_m,st=1;
for(int i=0;i<m;i++){
int x=_u[i]+1,y=_v[i]+1;
e[y].push_back(x);
S[x].insert(make_pair(y,i));
}
if (!init())return (bool)0;
v0=ans,reverse(v0.begin(),v0.end());
for(int i=1;i<=n;i++)to[i]=(*S[i].begin());
int id=to[st].second;
pair<int,int>ot=(*--S[st].end());
for(int i=0;i<4;i++){
calc(),swap(ot,to[st]);
if ((i==2)&&(id==to[st].second))break;
}
for(int i:v0)ans.push_back(i);
return ans;
}