[日常训练] Graph

来源:互联网 发布:windows live登录 编辑:程序博客网 时间:2024/06/05 08:03

Time limit: 2s Memory limit: 512MB

Description

  • 给定一张n个点m条边的无向图,每条边连接两个顶点,保证无重边自环,不保证连通
  • 你想在这张图上进行若干次旅游,每次旅游可以任选一个点x作为起点,再走到一个与x直接有边相连的点y,再走到一个与y直接有边相连的点z并结束本次旅游
  • 作为一个旅游爱好者,你不希望经过任意一条边超过一次,注意一条边不能即正向走一次又反向走一次,注意点可以经过多次,在满足此条件下,你希望进行尽可能多次的旅游,请计算出最多能进行的旅游次数并输出任意一种方案

Input Format

  • 1行两个正整数nm,表示全图的点数与边数下接m行,每行两个数字uv表示一条边

Output Format

  • 1行一个整数cnt表示答案下接cnt行,每行三个数字x,y,z,表示一次旅游的路线如有多种旅行方案,任意输出一种即可

Sample Input

  • 4 5 1 2 3 2 2 4 3 4 4 1

Sample Output

  • 2 4 1 2 4 3 2

Constraints

  • 对于前 20% 的数据,n10,m20
  • 对于令 20% 的数据,m=n1,并且图连通
  • 对于令 10% 的数据,每个点的度数不超过2
  • 对于 100% 的数据,n100000,m200000

Solution 构造 + DFS

  • 显然,题目是要我们尽可能多地找出像下面这样的“东西”把整张图覆盖完
  • 考虑到这里有一个公共点y,我们不妨把这两条边看作是分配到点y
  • 那么显然,当一个点分配的边数为偶数时,我们可以通过两两任意组合构造出一种匹配完所有边的方案,为奇数时则会剩下一条边
  • 进一步的,我们可以通过这一性质构造出一种最优方案
  • 首先说一下结论:对于一个边数为m连通快,最优方案必然有m2条旅行路线
  • 我们先对这张图构造出一棵DFS树,显然图中有非树边和树边两种边
  • 然后按照DFS的顺序处理分配方案,也就是处理到某一节点x时,它子树中所有点的分配方案已经处理完了
  • 那么此时对于这个点x:它所能选择的边有它的父亲边,它未被选择过的儿子边以及连向它的未被选择过的非树边
  • 我们把后两者先分配到点x,那么我们就总能通过调整父亲边是否分配给点x来使得点x分配到的边数固定为偶数
  • 于是最后只可能在根节点处剩下一条边,并且是在总边数为奇数的情况下,则我们上面所说的结论成立,也因此构造出了一组方案

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;}