• [多校联考2021] 模拟赛3


    题目描述

    本题中所有的图都指的是无重边无自环的无向图。

    定义正则图为每个点度数都相等的图,(k) 正则图为每个点度数都为 (k) 的图,偶正则图为每个点度数都为偶的图。

    有一张偶正则图,构造删边方案使之变成 (2) 正则图(就是若干个环)。

    (n,mleq 10^5)

    解法

    首先有一个经典问题,如果是有向图要求环的话,我们把每个点拆成入点和出点,跑二分图匹配就可以得到答案。

    但是这道题给的是无向图,那么给无向图的每条边定向就可以用有向图的方法了。注意到每个点的度数都是偶数且相等这个关键条件,可以跑原图的欧拉回路来定向,这样每个点就有 (k) 个入边和 (k) 个出边了。

    但是我们要证明转化过来的有向图也一定能求出解才算严谨。根据 ( t Hall) 定理:如果 (X) 部的任意若干个点至少和 (Y) 部等量的点相邻,那么这个图存在完备匹配。考虑 (X) 部的 (x) 个点,至少向 (Y) 部连了 (kx) 个边,如果对应的点数小于 (x) 说明存在度数大于 (k),矛盾,故一定会有解。

    最后再说一些关于欧拉回路的细节,因为这题的欧拉回路是要把每个边访问一遍,正常的欧拉回路是访问每个点,所以在 ( t dfs) 的时候要 充分的扩展,一条路走到黑是不行的,所以有一个地方要 ( t break)

    二分图匹配用 ( t dinic),总所周知复杂度是 (O(msqrt m)) 的。

    #include <cstdio>
    #include <vector>
    #include <queue>
    #include <map>
    using namespace std;
    const int M = 200005;
    const int inf = 0x3f3f3f3f;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,tot,s,t,f[M],cur[M],dis[M];
    vector<int> g[M];map<int,int> mp[M];
    struct edge
    {
    	int v,c,next;
    }e[10*M];
    void add(int u,int v,int c)
    {
    	e[++tot]=edge{v,c,f[u]},f[u]=tot;
    	e[++tot]=edge{u,0,f[v]},f[v]=tot;
    }
    void euler(int u)
    {
    	for(int i=0;i<g[u].size();i++)
    	{
    		int v=g[u][i];
    		if(!mp[u][v])
    		{
    			mp[u][v]=mp[v][u]=1;
    			add(u+n,v,1);//出点和入点连边
    			euler(v);
    			//break;
    		}
    	}
    }
    int bfs()
    {
    	queue<int> q;
    	for(int i=0;i<=t;i++) dis[i]=0;
    	q.push(s);dis[s]=1;
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		if(u==t) return 1;
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v;
    			if(!dis[v] && e[i].c>0)
    			{
    				dis[v]=dis[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    int dfs(int u,int ept)
    {
    	if(u==t) return ept;
    	int flow=0,tmp=0;
    	for(int &i=cur[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(dis[v]==dis[u]+1 && e[i].c>0)
    		{
    			tmp=dfs(v,min(ept,e[i].c));
    			if(!tmp) continue;
    			ept-=tmp;
    			flow+=tmp;
    			e[i].c-=tmp;
    			e[i^1].c+=tmp;
    			if(!ept) break;
    		}
    	}
    	return flow;
    }
    signed main()
    {
    	freopen("edg.in","r",stdin);
    	freopen("edg.out","w",stdout);
    	n=read();m=read();
    	s=0;t=2*n+1;tot=1;
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read();
    		g[u].push_back(v);
    		g[v].push_back(u); 
    	}
    	euler(1);
    	for(int i=1;i<=n;i++)
    		add(s,i+n,1),add(i,t,1);
    	while(bfs())
    	{
    		for(int i=0;i<=t;i++) cur[i]=f[i];
    		k+=dfs(s,inf);
    	}
    	for(int i=2;i<=tot;i++)
    	{
    		int u=e[i].v,v=e[i^1].v;//考虑的是反向边 
    		if(u<=2*n && u>n && v<=n && v>=1 && e[i].c)
    			mp[u-n][v]=mp[v][u-n]=0;
    	}
    	printf("%d
    ",m-k);
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<g[i].size();j++)
    			if(i<g[i][j] && mp[i][g[i][j]])
    				printf("%d %d
    ",i,g[i][j]);
    }
    
  • 相关阅读:
    XStream教程
    Log4j教程
    Java.io包
    Java输入/输出教程
    Java.math.BigDecimal.abs()方法
    数据类型转换
    JUnit教程
    java.lang
    标识符
    PHP面向对象笔记解析
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14611845.html
Copyright © 2020-2023  润新知