Gym-102001,2018 ICPC Asia Jakarta Regional Contest K Boomerangs
题意
给一个\(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;
}