Promenade by the lake(建立欧拉回路)

原题: https://cn.vjudge.net/problem/Gym-101879C

题意: 已有几条边,求是否可以添加几条边使其成为一个欧拉回路(每个点的度为偶数)

解析:

  1. 对于一个连通块(加完所有边后),如果有奇数个奇点,那么显然是不可能使此块变成欧拉回路的
  2. 假设添加一条路径1->2->3->4,显然只会改变起点和终点的度的奇偶。那么我们要找的就是从一个奇点连向另外一个奇点的路径。
  3. 对于两条这样的路径,如果有一部分路径被重复使用,相当于没有使用,即1->2->3->4和5->2->3->6,那么一定可以写成1->2->5和4->3->6,也就是说一定可以交换匹配的奇点来实现

综合以上三点,得出,在每个连通块内(考虑合法即偶数个奇点),假设一条边把连通块分成两个奇数个奇点的连通块,那么这条边一定是需要连接的,而分成两个偶数块按照第三点来说,是一定不会被连接的

代码:

#include
using namespace std;
const int N=2*3e5+5;

int n,m;
int deg[N];

int from[N],to[N],head[N],nex[N];int now;
void add(int a,int b){
    from[++now]=a,to[now]=b;nex[now]=head[a];head[a]=now;
}


int fa[N],tmp[N];//用并查集变成树型
int fi(int x){
    return fa[x]==x?x:fa[x]=fi(fa[x]);
}
void un(int a,int b){
    int f1=fi(a),f2=fi(b);
    if(f1!=f2){
        fa[f1]=f2;
        tmp[f2]+=tmp[f1];
    }
}

int siz[N];//子树
bool vis[N];
vector<int>ans;
void dfs(int p){
    siz[p]=deg[p];
    vis[p]=1;
    for(int i=head[p];i;i=nex[i]){
        if(vis[to[i]])continue;
        dfs(to[i]);
        siz[p]+=siz[to[i]];
        if(siz[to[i]]&1){//子树有奇数个奇点
            ans.push_back(i);
        }
    }
}

int main(){
    int t;scanf("%d%d%d",&n,&t,&m);
    while(t--){
        int a,b;scanf("%d%d",&a,&b);
        deg[a]^=1,deg[b]^=1;
    }
    for(int i=1;i<=n;i++)fa[i]=i,tmp[i]=deg[i];
    for(int i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        un(a,b);add(a,b);add(b,a);
    }
    for(int i=1;i<=n;i++){
        if(fi(i)!=i)continue;
        if(tmp[i]&1)return 0*printf("NO\n");//连通块为奇数块
        dfs(i);
    }
    printf("YES\n%d\n",ans.size());
    for(auto i:ans){
        printf("%d %d\n",from[i],to[i]);
    }
}

你可能感兴趣的:(图论/搜索)