题目描述
原题来自:UOJ #117
有一天一位灵魂画师画了一张图,现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。
一共两个子任务:
-
这张图是无向图。(5050 分)
-
这张图是有向图。(5050 分)
输入格式
第一行一个整数 tt,表示子任务编号。t∈1,2t∈1,2,如果 t=1t=1 则表示处理无向图的情况,如果 t=2t=2 则表示处理有向图的情况。
第二行两个整数 n,mn,m,表示图的结点数和边数。
接下来 mm 行中,第 ii 行两个整数 vi,uivi,ui,表示第 ii 条边(从 11 开始编号)。保证 1≤vi,ui≤n1≤vi,ui≤n。
-
如果 t=1t=1 则表示 vivi 到 uiui 有一条无向边。
-
如果 t=2t=2 则表示 vivi 到 uiui 有一条有向边。
图中可能有重边也可能有自环。
输出格式
如果不可以一笔画,输出一行 NO
。
否则,输出一行 YES
,接下来一行输出一组方案。
-
如果 t=1t=1,输出 mm 个整数 p1,p2,…,pmp1,p2,…,pm。令 e=|pi|e=|pi|,那么 ee 表示经过的第 ii 条边的编号。如果 pipi为正数表示从 veve 走到 ueue,否则表示从 ueue 走到 veve。
-
如果 t=2t=2,输出 mm 个整数 p1,p2,…,pmp1,p2,…,pm。其中 pipi 表示经过的第 ii 条边的编号。
样例 1
Input | Output |
---|---|
1 3 3 1 2 2 3 1 3 | YES 1 2 -3 |
样例 2
Input | Output |
---|---|
2 5 6 2 3 2 5 3 4 1 2 4 2 5 1 | YES 4 1 3 5 2 6 |
数据范围与提示
1≤n≤105,0≤m≤2×105
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stack>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
vector<int > vs;
int k=0,op,n,m,out[110100],in[1100001],first[1101000],next1[1101000],u[1100100],v[1100100],book[1100109];
void add(int x,int y)
{
u[k]=x,v[k]=y;
next1[k]=first[x],first[x]=k++;
}
void dfs(int x)
{
int v1,t;
for(int &i=first[x]; i!=-1;)/*i起着一个遍历和删边两个作用*/
{
if(book[i])/*x这个点是可以重复遍历的(因为欧拉路径只要求边不重复)
因此上次标记过的边再去遍历太浪费时间了*/
{
i=next1[i];
/*j代表的是上一条边的下标(&i=next[j]是本条边)因为i直接获取了next[j]的地址,
i=next[i]相当于直接让next[j]=next[next[j]],意思就是直接跳过本条边的遍历
相当于直接删除本边*/
continue;
}
book[i]=1;
if(op==1)
{
book[i^1]=1;/*标记同一个无向边*/
/*
规律:0^1=1 ; 0 1是一对互相反向的边
1^1=0;
2^1=3; 2 3是一对互相反向的边
3^1=2;
4^1=5; 4 5是一对互相反向的边
5^1=4;
…………
无向图中通过0可以找到反向边1; 前提边的下标只能从0开始存
*/
t=i/2+1;
/*
无向图中:
0 1是一对互相反向的边 0/2=1/2=0 ;
2 3是一对互相反向的边 2/2=3/2=1;
4 5是一对互相反向的边 4/2=5/2=2;
………………………………………………
t=i/2+1 表示是第t条无向边
*/
if(i%2) t=-t;
}
else
t=i+1;
v1=v[i];
i=next1[i];
dfs(v1);
vs.push_back(t);
}
}
int pan()
{
int i,j;
for(i=1; i<=n; i++)
{
if(op==1&&(in[i]+out[i])%2) return 0;
if(op==2&&in[i]!=out[i]) return 0;
}
for(i=1; i<=n; i++)
{
if(first[i]!=-1)
{
/*找到一个含边的点,有的点没边完全可以不遍历(欧拉路径只需要遍历所有边)*/
dfs(i);
break;
}
}
if(vs.size()<m) return 0;/*检查是否遍历到所有边*/
return 1;
}
int main()
{
int i,j,a1,b1;
scanf("%d%d%d",&op,&n,&m);
memset(first,-1,sizeof(first));
k=0;
vs.clear();
for(i=1; i<=m; i++)
{
scanf("%d%d",&a1,&b1);
add(a1,b1);
if(op==1) add(b1,a1);
out[a1]++,in[b1]++;
}
if(!pan())
{
printf("NO\n");
return 0;
}
else
{
printf("YES\n");
for(i=vs.size()-1; i>=0; i--)
{
printf("%d%c",vs[i],i==0?'\n':' ');
}
}
}