对于一个结点的儿子,两两配对。所以如果是偶数个就正好。
对于不正好的,让一条边上传,借用到父节点的那条边。一直这么传上去。
if(!fa[v]){
fa[v]=u;
q[++t]=v;
}//找一下没有被借用的儿子边。
if(v!=fa[u]&&!vis[i]){
B[++Neko]=i;
}//把合法的儿子边都加进来
if(v==fa[u]&&!vis[i])B[++Neko]=i;//找一下备用的父亲边
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define int long long
using namespace std;
const int maxn=4e5+1;
int n,m;
int fa[maxn];
int len=0,head[maxn];
int B[maxn],vis[maxn];
int P5=0;
int Fr(int x){return (x&1)?x+1:x-1;}
struct E{int to,next;}e[maxn];
struct E1{int x,y,z;}ans[maxn];
void A(int x,int y){e[++len].to=y;e[len].next=head[x];head[x]=len;}
int q[maxn];
void Dfs(int S){
int h=0,t=1;
fa[S]=-1;
q[1]=S;
while(h<t){
int u=q[++h];
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!fa[v]){
fa[v]=u;
q[++t]=v;
}
}
}
while(t--){
int u=q[t+1];
int Neko=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v!=fa[u]&&!vis[i]){
B[++Neko]=i;
}
}
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa[u]&&!vis[i])B[++Neko]=i;
}
for(int i=1;i+1<=Neko;i+=2){
vis[B[i]]=1;vis[Fr(B[i])]=1;
vis[B[i+1]]=1;vis[Fr(B[i+1])]=1;
ans[++P5].x=e[B[i]].to;
ans[P5].y=u;
ans[P5].z=e[B[i+1]].to;
}
}
}
signed main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++){
int v,u;scanf("%lld%lld",&u,&v);
A(u,v);A(v,u);
}
for(int i=1;i<=n;i++){
if(!fa[i])Dfs(i);
}printf("%lld
",P5);
for(int i=1;i<=P5;i++){
printf("%lld %lld %lld
",ans[i].x,ans[i].y,ans[i].z);
}
return 0;
}