codeforces 858F Wizard's Tour

来源:互联网 发布:2018php工作 编辑:程序博客网 时间:2024/05/18 17:41

原题地址: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("");}}