[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;
}
posted @ 2023-01-22 12:24  PYWBKTDA  阅读(70)  评论(0编辑  收藏  举报