[省选前题目整理][CodeForces 527E]Data Center Drama(欧拉回路+构造)

题目链接

http://codeforces.com/contest/527/problem/E

思路

非常不错的一道图论的构造题。
注意到最终的图是一个欧拉回路,那么每个点最终的入度等于出度,如果这个欧拉回路长是偶数的话,就相当于把这个图变成无向图(所有边加上反向边),并且新图的每个点的度数均为偶数。因此我们就需要在原图中,对于所有度数为奇数的点,依次连无向边,这样原图就变成了所有点度数均为偶数了,然后再对这个无向图,随便找个起点做欧拉回路,得到这个欧拉回路的路径,要把这个无向图的欧拉回路变成一个有向图的欧拉回路就很容易了,假如原来无向图的欧拉回路是a-b-c-d-e这样的形式,只需要把它变成a->b<-c->d<-e……这样的形式,就是给原来的无向图的每条边确定了方向,这样做把每个点在无向图中的偶数的度数均匀地分成了一半出度、一半入度,构造出的新有向图就是一个欧拉回路 了,但是前提就是这个欧拉回路的长度为偶数,这是很显然的。
那么如果把奇数度数点依次加完边后无向边总数为奇数怎么办?随便找个点加自环即可。

但是此题还有很重要的一点需要注意:做欧拉回路时,如果不做优化,把之前访问过的废边都访问的话,整个程序的复杂度就是∑degree[i]2,会TLE,因此要在做欧拉回路时,每访问完一条边,就把这条边删掉,最好实现的方式就是用multiset当作邻接表存边了(因为有自环所以不能用set),另外自环的边也是要加两遍的。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>
#include <vector>

#define MAXV 610000

using namespace std;

vector<int>odd,ans;
multiset<int>G[MAXV];

int tot_edge=0;
int degree[MAXV]; //每个点的度

inline void AddEdge(int U,int V)
{
    G[U].insert(V);
}

void DFS(int u)
{
    while(!G[u].empty())
    {
        int v=*G[u].begin();
        G[u].erase(G[u].begin());
        G[v].erase(G[v].find(u));
        DFS(v);
    }
    ans.push_back(u);
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        AddEdge(u,v),tot_edge++;
        AddEdge(v,u);
        degree[v]++,degree[u]++;
    }
    for(int i=1;i<=n;i++)
        if(degree[i]&1)
            odd.push_back(i);
    for(int i=0;i<(int)odd.size();i+=2)
    {
        int u=odd[i],v=odd[i+1];
        AddEdge(u,v),tot_edge++;
        AddEdge(v,u);
    }
    if(tot_edge&1) //需要加一条自环边
        tot_edge++,AddEdge(1,1),AddEdge(1,1);
    DFS(1);
    printf("%d\n",tot_edge);
    for(int i=0;i<(int)ans.size()-1;i++)
    {
        if(i&1) printf("%d %d\n",ans[i],ans[i+1]);
        else printf("%d %d\n",ans[i+1],ans[i]);
    }
    return 0;
}

你可能感兴趣的:([省选前题目整理][CodeForces 527E]Data Center Drama(欧拉回路+构造))