题意
给定n个点m条边的无向连通图G(m为偶数),已知有k个点(k为偶数)度数为奇数。现在你要把它们两两配对,然后用k/2条连接这些点对的包含偶数条边的路径覆盖完G,需要保证每条边都恰好被覆盖一次(一条路径可以经过相同点)。无解输出NIE
1≤n,m≤250,000
分析
这道题很神
首先我们考虑如果没有路径长度为偶数应该怎么做,就是我们可以建一个新的点root,然后把奇数的点连上去,这样所有的度数都是偶数,只要找到欧拉回路,两个奇数点之间就是答案
现在多了一个路径长度为偶数的条件,我们把每个点拆成两个点i和i+n,把root和i连边
然后就像二分图一样交错连边,但是现在要保证度数都是偶数
我们可以这样保证:首先把原图的任意一个生成树给提出来,剩下的边随便连,而生成树上的边可以调度数,我们在生成树上从1开始搜索
假设有一条树边(x,fa),那么如果cnt[x] 是奇数,那么就在新图上连(x,fa+n)
否则的话就连(x+n,fa)
这样的话只有1这个点的度数可能会有问题,但是我们想想,首先1和n+1的度数是奇偶性相同的,因为其它的点的度数都是偶数,再其次,题目有边数是偶数的条件,我们的建边是按二分图来建的,根据二分图的性质,1和n+1肯定是偶数,因为从一边开始走,所有点度数是偶数,肯定能回到这一边的
之后的话我们在新图跑一遍欧拉回路就可以了,又考虑刚才的正确性
一些边建在(x,y+n)或(x+n,y),其实只要考虑有一条欧拉同路,那些边建在哪里只是代表不同的方案而已
代码
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 500010;
inline int read()
{
char ch=getchar(); int p=0; int f=1;
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node
{
int x,y,next,id;
}edge[N<<1]; int len,first[N]; int cnt[N];
void ins(int x,int y,int id)
{
len++; edge[len].x=x; edge[len].y=y; edge[len].id=id; edge[len].next=first[x]; first[x]=len;
len++; edge[len].x=y; edge[len].y=x; edge[len].id=id; edge[len].next=first[y]; first[y]=len;
cnt[x]++; cnt[y]++;
}
int fa[N];
int Find(int x){return (fa[x]==x)?fa[x]:fa[x]=Find(fa[x]);}
vector<int> v[N],vi[N]; int n,m;
void build(int x,int f,int id)
{
for(int i=0;i<v[x].size();i++) if(v[x][i] != f) build(v[x][i],x,vi[x][i]);
if(f)
{
if(cnt[x] & 1) ins(x,(f==0) ? f : f+n,id);
else ins(x+n,f,id);
}
}
int s[N],top=0; bool vis[N];
void dfs(int x)
{
while(1)
{
int k = first[x]; if(k==-1) break; first[x] = edge[k].next;
if(vis[k>>1]) continue; vis[k>>1] = 1;
dfs(edge[k].y);
s[++top] = edge[k].id;
}
}
int d[N];
int main()
{
n = read(); m = read();
len = 1; memset(first,-1,sizeof(first));
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
int x=read(); int y=read(); d[x]++; d[y]++;
if(Find(x) == Find(y)) ins(x,y+n,i);
else
{
fa[Find(x)] = Find(y);
v[x].pb(y); v[y].pb(x);
vi[x].pb(i); vi[y].pb(i);
}
}
for(int i=1;i<=n;i++) if(d[i] & 1) ins(0,i,m+i);
build(1,0,0);
dfs(0);
while(top > 0)
{
int x=s[top--]-m;
int j=top; while(s[j] <= m && j>1) j--;
int y=s[j]-m;
printf("%d %d %d\n",x,y,top-j);
for(int i=top;i>j;i--) printf("%d%c",s[i]," \n"[i==j+1]);
top = j-1;
}
return 0;
}