看到题解没有,来一发空间、时间均为 \(O(n+m)\) 的做法。
请使用 vector
存图。链式前向星爬
明显有两个结论:
存在欧拉路径的充要条件是:
1.该有向图的基图联通。(已保证)
2.各有 \(1\) 或 \(0\) 个出度 \(-\) 入度、入度 \(-\) 出度 \(=1\) 的点。
先用这个判 No
。
字典序最小,对于每一个点连出的边按另一个端点排序即可。
dfs
解决。得用个当前弧。
排序优化。
\(O(n)\) 的排序:计数、桶、基数。
wtcl,我只会桶。当然你要基数也是珂以的。
但是每个点都扫描一次桶,TLE 等着你。
一次性把所有点存进去。不能像一般的那样 \(+1\),要存入他的编号。
一个桶需要开 vector
。总共只会有 \(m\) 个存入,不会 MLE
。
具体见代码。
这个空间、时间均为下限。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
vector<int> e[maxn],t[maxn];
stack<int> st;
int In[maxn],Out[maxn],cur[maxn];
int n,m,cnt1=0,cnt2=0;
void rt(){
cout<<"No"<<endl;
exit(1);
/*
想学习题解的同学,请把这里改成 0。否则 RE 。
*/
}
void dfs(int u){
for(int i=cur[u];i<e[u].size();i=cur[u]){ //当前弧
cur[u]=i+1;
dfs(e[u][i]);
}
st.push(u);
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
In[v]++,Out[u]++;
t[v].push_back(u);
}
int cnt1=0,cnt2=0,pd=0,start=1;
for(int i=1;i<=n;i++){
if(Out[i]!=In[i])pd=1;
if(Out[i]-In[i]>1)rt();//因为可以认为有一个出边永远不可能被遍历到
if(Out[i]-In[i]==1)start=i,cnt1++;
if(In[i]-Out[i]==1)cnt2++;
}
if(!(pd==0||((cnt1==cnt2)&&cnt1==1)))rt();
for(int i=1;i<=n;i++){
for(auto x:t[i])e[x].push_back(i);
}
memset(cur,0,sizeof(cur));
dfs(start);
while(!st.empty()){
cout<<st.top()<<" ";
st.pop();
}
return 0;
}
附:写的时候的奇怪的事情
我因为 dfs
初始参数写成 \(1\) 交了很多遍。
最优解那个真的玄学。为什么我的卡过的 \(O(n+m)\) 没有卡过的 \(O(n+m\log m)\) 快……
不过也没事。毕竟,普通平衡树和其加强版的最优解不是平衡树,线性筛素数的最优解不是欧氏筛……