原题链接:http://codeforces.com/problemset/problem/848/C
大致题意:给出n个点,m条边的图,每次删去有公共点的2条边,问最多删几次,及其中一种方案。
先简化一下题意,如果给出的是一颗树,对于某一个节点,如果它向下有2*k条边,则可以直接把这2*k条边两两配对删去;如果有2*k+1条边,则将剩下的一条边和连向这个节点的父亲的边配对删去,如果是根节点则直接放弃。显然,这样的执行方案是最优的,可以配对删去(n-1) div 2 次。
然后如果当前节点有一些既不是儿子边也不是父亲边的边,则可以当做连向其儿子节点的边处理,因为这种边和它的儿子边一样符合配对的要求,所以可以直接当做儿子边处理。
代码:
#include <bits/stdc++.h>
using namespace std;
inline void read(int &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void read(long long &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void write(int x){
static const int maxlen=100;
static char s[maxlen];
if (x<0) { putchar('-'); x=-x;}
if(!x){ putchar('0'); return; }
int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
for(int i=len-1;i>=0;--i) putchar(s[i]);
}
const int MAXN = 310000;
const int MAXM = 610000;
int n,m;
int pre[ MAXM ],now [ MAXN ], ed [ MAXM ] , tot;
int st[ MAXM ];
bool use[ MAXM ];
void build(int a,int b){
pre[++tot]=now[a];
now[a]=tot;
ed[tot]=b;
st[tot]=a;
}
map<int,int> M;
int dfs(int x,int fa){
int last=0;
for (int p=now[x];p;p=pre[p])
if ( !use[ p/2 ] )
{
use[ p/2 ]=1;
int tmp=0;
tmp=dfs( ed[p] , x );
if (tmp!=0)
{
M[tmp]=p;
}
else
if (last)
{
M[last]=p^1;
last=0;
}
else
last=p;
}
return last;
}
int main(){
read(n); read(m);
tot=1;
for (int i=1;i<=m;i++)
{
int a,b;
read(a); read(b);
build(a,b);
build(b,a);
}
for (int i=1;i<=n;i++)
dfs(i,0);
printf("%d\n",M.size());
for ( auto S:M)
printf("%d %d %d\n",ed[ S.first ] , st[ S.first ] , st[ S.second ]);
return 0;
}