原题地址:http://codeforces.com/problemset/problem/858/F
一开始我居然想的是一道匹配题,硬是YY了一种对于边的一般图匹配。就.....炸了qwq。
后来发现这可以拿树dp来贪心做owo。
不xia扯了开始分析:
为了方便讨论我们先假装原图是一个树形图。
那么如图:对于节点u,我们能找到一条路径的话只会有两种情况:
第一种就是如左图所示,u可以找到与其相连的点v, v可以找到与其相连的另一个点w,这样可以组成一条路径。
第二种就是右图。u能够找到两个与其相连的点u,v来组成v-u-w的路径。
显然左图需要讨论2层,而右图只需要讨论一层,并且只能讨论一次,所以在对一个节点分析的时候我们优先考虑右情况,否则会出现这种情况:
会因为先考虑左情况而找不到最优解。
然后就可以瞎YY了qwq。
对于u可连接到的节点数为偶数的点,我们让这些点经过u来形成一条路。
对于u可连接到的节点数为奇数的点,我们让这些点形成路的同时,留下一个点和u与u的父亲节点形成一条路(如果u找不到一个合法父亲节点了呢??那就只能gg咯)。
这是对于树的情况。
那么图的情况呢???
好像是以样的,但是有一些坑点:
1. 在给一个边做标记的时候要给反向边也打一个标记。(调了好久)
2.在遍历到一个节点的时候一定要先把这个节点的所有边都打上标记,不然会重复讨论。
除此之外就没什么了。对于多个块的话分别dfs就好,输出方案的话也没什么太多要注意的。
ps。这题调出来的时候cf已经结束了一个小时了qwq。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 200000 + 10;
struct edge {
int u, v;
bool vis;
void init(int u, int v) {
this->u = u, this->v = v;
}
}e[MAXN<<1];
int ec;
vector<int>g[MAXN];
int m, n;
bool vis[MAXN];
struct query {
int u, v, w;
void init(int u, int v, int w) {
this->u = u, this->v = v, this->w = w;
}
void write() {
printf("%d %d %d", u, v, w);
}
}q[MAXN];
int qc;
int dfs(int u) {
vis[u] = 1;
vector<int>vec;
vector<int>to;
for (int i=0; i<g[u].size(); ++i) {
int id = g[u][i];
if (e[id].vis == 0) {
e[id].vis = 1;
e[id^1].vis = 1;
to.push_back(e[id].v);
}
}
for (int i=0; i<to.size(); ++i) {
int v = to[i];
int w = dfs(v);
if (w != 0) {
q[qc++].init(u, v, w);
} else {
vec.push_back(v);
}
}
int i;
for (i=0; i+1<vec.size(); i+=2) {
int v = vec[i];
int w = vec[i];
q[qc++].init(v, u, w);
}
if (i == vec.size()) return 0;
else return vec[i];
}
int main() {
scanf("%d%d", &n, &m);
for (int i=0; i<m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(ec);
e[ec++].init(u, v);
g[v].push_back(ec);
e[ec++].init(v, u);
}
for (int i=1; i<=n; ++i) {
if (!vis[i]) {
dfs(i);
}
}
printf("%d\n", qc);
for (int i=0; i<qc; ++i) {
q[i].write();
puts("");
}
}