Time limit: 2s Memory limit: 512MB
Description
- 给定一张
n
个点m条边的无向图,每条边连接两个顶点,保证无重边自环,不保证连通
- 你想在这张图上进行若干次旅游,每次旅游可以任选一个点
x
作为起点,再走到一个与x直接有边相连的点
y
,再走到一个与y直接有边相连的点
z
并结束本次旅游
- 作为一个旅游爱好者,你不希望经过任意一条边超过一次,注意一条边不能即正向走一次又反向走一次,注意点可以经过多次,在满足此条件下,你希望进行尽可能多次的旅游,请计算出最多能进行的旅游次数并输出任意一种方案
- 第1行两个正整数
n
与m,表示全图的点数与边数下接
m
行,每行两个数字u与
v
表示一条边
- 第1行一个整数
cnt
表示答案下接
cnt
行,每行三个数字
x,y,z
,表示一次旅游的路线如有多种旅行方案,任意输出一种即可
Sample Output
Constraints
- 对于前 20% 的数据,
n≤10,m≤20
- 对于令 20% 的数据,
m=n−1
,并且图连通
- 对于令 10% 的数据,每个点的度数不超过
2
- 对于 100% 的数据,n≤100000,m≤200000
Solution 构造 + DFS
- 显然,题目是要我们尽可能多地找出像下面这样的“东西”把整张图覆盖完
- 考虑到这里有一个公共点
y
,我们不妨把这两条边看作是分配到点y上
- 那么显然,当一个点分配的边数为偶数时,我们可以通过两两任意组合构造出一种匹配完所有边的方案,为奇数时则会剩下一条边
- 进一步的,我们可以通过这一性质构造出一种最优方案
- 首先说一下结论:对于一个边数为
m
的连通快,最优方案必然有⌊m2⌋条旅行路线
- 我们先对这张图构造出一棵
DFS
树,显然图中有非树边和树边两种边
- 然后按照
DFS
的顺序处理分配方案,也就是处理到某一节点
x
时,它子树中所有点的分配方案已经处理完了
- 那么此时对于这个点x:它所能选择的边有它的父亲边,它未被选择过的儿子边以及连向它的未被选择过的非树边
- 我们把后两者先分配到点
x
,那么我们就总能通过调整父亲边是否分配给点x来使得点
x
<script type="math/tex" id="MathJax-Element-2095">x</script>分配到的边数固定为偶数
- 于是最后只可能在根节点处剩下一条边,并且是在总边数为奇数的情况下,则我们上面所说的结论成立,也因此构造出了一组方案
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int S = 1 << 20;
char frd[S], *hed = frd + S;
const char *tal = hed;
inline char nxtChar()
{
if (hed == tal)
fread(frd, 1, S, stdin), hed = frd;
return *hed++;
}
inline int get()
{
char ch; int res = 0;
while (!isdigit(ch = nxtChar()));
res = ch ^ 48;
while (isdigit(ch = nxtChar()))
res = res * 10 + ch - 48;
return res;
}
inline void put(int x)
{
if (x > 9) put(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e5 + 5, M = N << 2;
int n, m, len, Ans;
bool vis[N], stp[M];
struct Edge
{
int num, to; Edge *nxt;
};
Edge p[M], *T = p, *lst[N];
Edge q[M], *Q = q, *rst[N];
inline void LinkEdge(int x, int y, int z)
{
(++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->num = z;
(++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->num = z;
}
inline void RinkEdge(int x, int y)
{
(++Q)->nxt = rst[x]; rst[x] = Q; Q->to = y; ++len;
}
inline void Dfs(int x, int fa, int I)
{
int cnt = 0, y;
for (Edge *e = lst[x]; e; e = e->nxt)
{
if (vis[y = e->to])
{
if (y == fa || stp[e->num]) continue;
++cnt;
stp[e->num] = true;
RinkEdge(x, y);
continue;
}
vis[y] = true;
Dfs(y, x, e->num);
}
for (Edge *e = lst[x]; e; e = e->nxt)
if (vis[y = e->to] && !stp[e->num])
{
if (y == fa) continue;
++cnt;
stp[e->num] = true;
RinkEdge(x, y);
}
if ((cnt & 1) && fa)
stp[I] = true, RinkEdge(x, fa);
}
int main()
{
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
n = get(); m = get(); int x, y;
for (int i = 1; i <= m; ++i)
{
x = get(); y = get();
LinkEdge(x, y, i);
}
for (int i = 1; i <= n; ++i)
if (!vis[i])
{
len = 0; vis[i] = true;
Dfs(i, 0, 0);
Ans += len >> 1;
}
put(Ans), putchar('\n');
for (int i = 1; i <= n; ++i)
for (Edge *e = rst[i]; e && e->nxt; e = e->nxt->nxt)
{
put(e->to), putchar(' ');
put(i), putchar(' ');
put(e->nxt->to), putchar('\n');
}
fclose(stdin); fclose(stdout);
return 0;
}