首先考虑第一问。每个联通块的情况是相对独立的,所以可以分别求每个联通块的答案。无向图中存在欧拉路的条件是奇点数为0或2,那么合法方案肯定是tp到一个奇点,通过一条欧拉路到另一个奇点,再tp到另一个奇点……
设共k个联通块,第$i$个里奇点个数为$c_i$,那么答案即为$sum_{i=1}^k max(1,frac c2)-1$,最后-1是因为选起点不用浪费传送次数。
关于构造方案,我们先对于每个联通块求它内部的奇点。如果没有的话直接跑欧拉路即可。
如果有奇点,那么必有偶数个,因为每个联通块的点的度数之和必为偶数。可以新建一个源点,向所有奇点连边,再跑欧拉路。
最后的方案输出:
如果从源点到某个点,那这个点一定是奇点,操作为1 x。
从某个点跑到源点,显然不用管。
从点x跑到点y,操作为0 y。
另外,写暴力圈套圈的欧拉路算法还是要用非递归版的,直接dfs有可能爆栈也可能直接T掉(递归很慢)。
#include<cstdio> #include<iostream> #include<cstring> #define pa pair<int,int> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } const int N=1e5+5; int n,m; int to[N*10],nxt[N*10],head[N],tot=1,deg[N]; int st[N*10],top,vis[N],v[N*10]; int ans=0,cnt; pa res[N*10]; void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; deg[x]++; } void dfs(int x) { vis[x]=1; if(deg[x]&1)st[++top]=x; for(int i=head[x];i;i=nxt[i]) if(!vis[to[i]])dfs(to[i]); return ; } int syst[N*10],systop; void euler() { systop=0; syst[++systop]=0; while(systop>0) { int x=syst[systop],i=head[x]; while(i&&v[i])i=nxt[i]; if(i) { syst[++systop]=to[i]; v[i]=v[i^1]=1; head[x]=nxt[i]; } else systop--,st[++top]=x; } } int main() { n=read();m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); add(x,y);add(y,x); } int ans=0; for(int i=1;i<=n;i++) { if(deg[i]&&!vis[i]) { top=0; dfs(i); head[0]=0; if(!top)add(0,i),add(i,0),add(i,0),add(0,i); else while(top){int now=st[top--];add(0,now);add(now,0);}; euler(); int now; while(top>1) { now=st[top--]; if(now) res[++cnt]=make_pair(0,now); else res[++cnt]=make_pair(1,st[top--]),ans++; } } } printf("%d %d ",ans-1,res[1].second); for(int i=2;i<=cnt;i++) printf("%d %d ",res[i].first,res[i].second); return 0; }