题目描述
题解
首先考虑如果每个点的度都是3的情况,可以发现只能是偶数个点,并且除去环上的边的话就是两两间有连边
于是我们加上环上一半且相间的边,不难发现这个图是个二分图,所以我们可以每条边都只走一遍就走完这张图,同理另一半也是一样,再加上走一个环,这样每条边就恰好经过了两边
现在考虑如果一个点的度大于3的话,可以考虑拆点,并且和原来编号的上一个拆的点相连接,这样我们就回到了度数都为3的情况了
具体的话可以画个图理解一下
代码
#includeusing namespace std; const int N=1e5+5; int T,n,m,c,hd[N],V[N],W[N],pr[N],in[N],vis[N],nx[N],t=1,is[N],k; vector<int>g[N],a[N]; void add(int u,int v,int w){ nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w; } void ins(int u,int v,int w){ add(u,v,w);add(v,u,w); } void bj(int u,int w){ vis[u]=1; for (int i=hd[u];i;i=nx[i]) if (W[i] && !vis[V[i]]) is[i]=is[i^1]=w,bj(V[i],w^1); } void go(int u,int w){ vis[u]=0;a[k].push_back(pr[u]); for (int i=hd[u];i;i=nx[i]) if (W[i]==w){ if (w && !is[i]) continue; if (!vis[V[i]]) continue; go(V[i],w^1); } } void work(){ scanf("%d%d",&n,&m);c=n; for (int i=1;i<=n;i++) g[i].clear(),g[i].push_back(i),pr[i]=i,in[i]=0; for (int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); if (u>v) swap(u,v); if (v==u+1 || (u==1 && v==n)) continue; if (in[u]) g[u].push_back(++c),pr[c]=u,u=c; if (in[v]) g[v].push_back(++c),pr[c]=v,v=c; ins(u,v,0);in[u]=in[v]=1; } for (int i=1,v,z;i<=n;i++){ v=i+1;if (v>n) v=1; z=g[i].size(); ins(g[i][z-1],v,1); for (int j=0;j 1;j++) ins(g[i][j],g[i][j+1],1); } k=1; for (int i=1;i<=n;i++) a[k].push_back(i); bj(1,0); for (int i=1;i<=c;i++) if (vis[i]) k++,go(i,0); bj(1,1); for (int i=1;i<=c;i++) if (vis[i]) k++,go(i,0); printf("%d\n",k); for (int l,z,i=1;i<=k;i++){ z=a[i].size();l=0; for (int j=1;j ) if (a[i][j]!=a[i][j-1]) l++,a[i][l]=a[i][j]; while(a[i][l]==a[i][0]) l--; printf("%d",l+1); for (int j=0;j<=l;j++) printf(" %d",a[i][j]); puts("");a[i].clear(); } for (int i=1;i<=c;i++) hd[i]=0;t=1; } int main(){ for (scanf("%d",&T);T--;work()); return 0; }