题意

给一个\(n\)个点\(m\)条边的无向图\(G=(V,E)\),让你找最多有多少个回力镖,回力镖是一个三元组\((u,v,w)\)表示边\((u,v)\subseteq E\)且边\((v,w)\subseteq E\),每个边只能存在于一个回力镖中。

分析

dfs深搜,回溯过程中将当前结点\(u\)的前向边两两组成回力镖,如果多出了一条边就和u的父亲组成回力镖,这样构造最多会浪费每次dfs的根节点连的一条前向边,所以总答案为每个连通块的边数向下整除2的和。

Code

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=1e5+10;
const int inf=1e9;
int n,m;
set<int>g[N],p[N];
int d[N],vis[N],f[N];
vector<pair<pii,int> >ans;
void dfs(int u){
	d[u]=d[f[u]]+1;
	for(int x:g[u]){
		if(vis[x]) continue;
		vis[x]=1;
		f[x]=u;
		dfs(x);
	}
	vector<int>q;
	for(int x:p[u]){
		if(x!=f[u]) q.pb(x);
	}
	for(int i=0;i+1<sz(q);i+=2){
		ans.pb(mp(mp(q[i],u),q[i+1]));
		p[u].erase(q[i]);
		p[u].erase(q[i+1]);
		p[q[i]].erase(u);
		p[q[i+1]].erase(u);
	}
	if(sz(q)%2!=0&&f[u]!=0){
		int x=q[sz(q)-1];
		ans.pb(mp(mp(x,u),f[u]));
		p[u].erase(x);
		p[x].erase(u);
		p[f[u]].erase(u);
		p[u].erase(f[u]);
	}
}
int main(){
	//ios::sync_with_stdio(false);
	//freopen("in","r",stdin);
	scanf("%d%d",&n,&m);
	rep(i,1,m){
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].insert(y);
		g[y].insert(x);
		p[x].insert(y);
		p[y].insert(x);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			vis[i]=1;
			dfs(i);
		}
	}
	printf("%d\n",sz(ans));
	for(auto x:ans){
		printf("%d %d %d\n",x.fi.fi,x.fi.se,x.se);
	}
	return 0;
}