题目
时间限制:1s 空间限制:256MB
有一天,一位灵魂画师画了一张n个点m条边(1≤n≤1e5,0≤m≤2e5)的图。
现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。
一共两个子任务:
- 这张图是无向图。(50分)
- 这张图是有向图。(50分)
图中可能有重边也可能有自环。
如果不可以一笔画,输出一行 “NO”。
否则,输出一行 “YES”,接下来一行输出一组方案。
- 如果 t=1,输出 m 个整数 p1,p2,…,pm。令 e=∣pi∣,那么 e 表示经过的第 i 条边的编号。如果 pi为正数表示从 ve 走到 ue,否则表示从 ue 走到 ve。
- 如果 t=2,输出 m 个整数 p1,p2,…,pm。其中 pi 表示经过的第 i 条边的编号。
思路来源
「UOJ#117」 欧拉回路 - qwertaya - 博客园
题解
经典Hierholzer算法,复杂度O(E),判断存不存在,先判度,再判图是连通图
有向图欧拉回路:图连通,一个环的情形(所有点入度出度相等),找环上一点输出路径
有向图欧拉路径:图连通,一个环或一条链的情形(所有点入度出度相等,或仅有恰有两个点,其中一个入度=出度+1,另一个出度=入度+1),找环上一点或链的起点输出路径
无向图欧拉回路:图连通,一个环的情形(所有点度都为偶数),找环上一点输出路径
无向图欧拉路径:图连通,一个环或一条链的情形(所有点度都为偶数,或仅有恰有两个度数为奇数的点),找环上一点或链的一端输出路径
欧拉回路性质:可以被拆成若干个环
心得
为什么欧拉回路后序输出?(即如下代码,搜完再输出)
void dfs(int u)
{
...
dfs(v);
printf("%d %d\n",v,u);
}
考虑1->2->3->2->1的情形,
如若前序,则可能会出现1->2,2->1,2->3,3->2的情况,不连续,故后序
此时a->b->c按v->u输出,会被输出为c->b->a,
如最终需输出a->b->c可将u->v压栈,倒序输出
①注意特判m=0,即没有边的情形
②注意弧优化,如若写成for(i=0;i<e[u].size();i++),会被以下样例卡成O(E*E)
20220213更新:可以用vector的pop_back,把用过的边pop掉,就不用记当前扫到第几条边了
1 200000
1 1
1 1
1 1
1 1
...
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<set>
using namespace std;
typedef pair<int,int> P;
const int N=1e5+10,M=2e5+10;
vector<P>e[N];
bool vis[M],used[N];
int ans[M],in[N],out[N],cnt;
int op,n,m,u,v;
void dfs(int u)
{
used[u]=1;
while(!e[u].empty())
{
P x=e[u].back();e[u].pop_back();
int v=x.first,id=x.second,fid=abs(id);
if(vis[fid])continue;
vis[fid]=1;
dfs(v);
ans[++cnt]=id;
}
}
bool ok()
{
if(m==0)return 1;//特判没边的情形
int pos=1;
if(op==2)
{
for(int i=1;i<=n;++i)
if(in[i]!=out[i])return 0;
else if(in[i])pos=i;
}
else
{
for(int i=1;i<=n;++i)
if((in[i]+out[i])%2)return 0;
else if(in[i]+out[i])pos=i;
}
dfs(pos);
for(int i=1;i<=n;++i)
if((in[i]||out[i])&&!used[i])return 0;
return 1;
}
int main()
{
scanf("%d",&op);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
e[u].push_back(P(v,i));
if(op==1)e[v].push_back(P(u,-i));
in[v]++;out[u]++;
}
if(!ok())puts("NO");
else
{
puts("YES");
for(int i=cnt;i>=1;--i)
{
if(op==2)ans[i]=abs(ans[i]);
printf("%d%c",ans[i],i==1?'\n':' ');
}
}
return 0;
}