UOJ168. 【UR #11】元旦老人与丛林

给出个无向图,问是否存在一种方式把边集划分成两个不为空的边集,使得两个边集分别都是森林。

(nle 2000,mle 2n)


有结论:当且仅当每个子图都满足(|E|le 2|V|-2)的时候,存在方案。

以下证明其充分性:

考虑归纳,即已经证明(|V|)更小的满足条件。以下称(|E|=2|V|-2)的图是满的

设当前图为(G)

对于1度点,可以直接删掉,连着的边任意分;对于2度点,也可以删掉,连着的边分在不同边集。又因(|E|le 2|V|-2),所以一定存在3度点。任意选一个3度点,记为(u),它连出去的三个点为(a,b,c)

(G_1)表示(G)中删去(u)形成的子图。

引理1:如果图中存在两个在点相交的满子图,那么它们的并也是满的。

设两个子图以及它们的交、并分别为(G_1=(V_1,E_1),G_2,G_3,G)

要证(|E|=2|V|-2)。由(|E|le 2|V|-2)(|E_1|+|E_2|-|E_3|le 2(|V_1|+|V_2|-|V_3|)-2)

(G_1,G_2)为满子图,联立上式得(|E_3|ge 2|V_3|-2)

又因为(|E_3|le 2|V_3|-2),所以(|E_3|=2|V_3|-2),进而(|E|=2|V|-2)

引理2(a,b,c)中至少存在一对点(x,y),使得(G_1)不存在满的子图同时包含(x,y)

反证。假设两两之间都存在一个满的子图包含它们,把这三个子图求并,由引理1得到这个并也是满的,有(|E|=2|V|-2)

往这个图中加入点(u)以及三条边,此时(|E|=2|V|-1),不符合。

构造方法如下:把((x,y))丢入(G_1)中,构造出方案之后,对于((x,y))所在的那个边集,把((x,y))改成((u,x),(u,y));另一个边集丢入剩下那条边。

接下来判定的时候,可以转化为最大权闭合非空子图问题:搞个(n+m)个点的图,对于一条边(e_i=(u,v)),连((e_i,u),(e_i,v)),给点赋权(-2),给边赋权(1),然后跑最大权闭合非空子图。如果跑出来结果大于(-2)则不合法。

因为子图要非空,直接跑只会跑到(0)。于是要枚举一个点,强制它选,并且将其权值变成(0),然后再跑最大权闭合子图。因为是二分图所以跑dinic时间是(O(nsqrt n m))

也可以用类似SDOI2014 LIS的网络流退流做法。时间是(O(sqrt nm+nm))


我的退流方法:

枚举(i),先判断((i,T))是否为割边,如果是,强制它选,答案一样,如果不是:

(F)为总流量,(f)((i,T))当前流量。将((i,T))清空,跑(flow(i,T,f))。新的流量可视作(F-f+flow(i,T,f))

可以看做强制不走这条边,用到达这条边的流量尝试跑到(T)

注意搞完之后跑(flow(T,i,f))回退。

int F=flow(s,t),mx=-2;
for (int i=1;i<=n;++i){
	if (!bfs(i,t))
		mx=max(mx,m-F);
	else{
		int f=rev(de[i])->c;
		de[i]->c=rev(de[i])->c=0;
		mx=max(mx,m-(F-f+flow(i,t,f))-2);
		flow(t,i,f);
		de[i]->c=2-f,rev(de[i])->c=f;
	}
	if (mx>-2)
		break;
}

人家的退流方法:

(flow(i,S,f)),再(flow(S,T))。就是直接退掉,然后再找新的增广路,不需要考虑是否为割边。

搞完之后把边加回来,也没必要立刻重新跑一次最大流,因为下一次影响答案前会跑一次(flow(S,T))

下面的代码之所以不规定(flow(i,S))的流量上限,是因为(i)的出边只有一条,((i,T))的流量等于(flow(i,S))

int F=flow(s,t),mx=-2;
for (int i=1;i<=n;++i){
	de[i]->c=rev(de[i])->c=0;
	F-=flow(i,s);
	F+=flow(s,t);
	mx=max(mx,m-F-2);
	de[i]->c=2;
	if (mx>-2)
		break;
}

贴一个总的代码:

using namespace std;
#include <bits/stdc++.h>
#define N 2009
#define INF 1000000000
int n,m;
int nn,s,t;
struct EDGE{
	int to,c;
	EDGE *las;
} e[N*7*2];
int ne;
EDGE *last[N*3];
void link(int u,int v,int c){
	e[ne]={v,c,last[u]};
	last[u]=e+ne++;
}
#define rev(ei) (e+(int((ei)-e)^1))
EDGE *de[N];
EDGE *cur[N*3];
int dis[N*3];
int S,T;
bool bfs(int s,int t){
	static queue<int> q;
	for (int i=1;i<=nn;++i)
		cur[i]=last[i];
	memset(dis,127,sizeof(int)*(nn+1));
	while (!q.empty()) q.pop();
	q.push(s);
	dis[s]=0;
	while (!q.empty()){
		int x=q.front();
		q.pop();
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->c && dis[ei->to]>=INF){
				dis[ei->to]=dis[x]+1;
				if (ei->to==t)
					return 1;
				q.push(ei->to);
			}
	}
	return 0;
}
int dfs(int x,int s){
	if (x==T)
		return s;
	int have=0;
	for (EDGE *&ei=cur[x];ei;ei=ei->las)
		if (ei->c && dis[ei->to]==dis[x]+1){
			int t=dfs(ei->to,min(ei->c,s-have));
			ei->c-=t,rev(ei)->c+=t,have+=t;
			if (have==s)
				return s;
		}
	return have;
}
int flow(int _S,int _T,int lim=INF){
	S=_S,T=_T;
	memset(cur,0,sizeof(EDGE*)*(nn+1));
	int res=0;
	while (lim && bfs(S,T)){
		int t=dfs(S,lim);
		res+=t,lim-=t;
	}
	return res;
}
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	nn=n+m;
	s=++nn,t=++nn;
	ne=0;
	memset(last,0,sizeof(EDGE*)*(nn+1));
	for (int i=1;i<=n;++i)
		de[i]=e+ne,link(i,t,2),link(t,i,0);
	for (int i=1,u,v;i<=m;++i){
		scanf("%d%d",&u,&v);
		link(s,i+n,1),link(i+n,s,0);
		link(i+n,u,INF),link(u,i+n,0);
		link(i+n,v,INF),link(v,i+n,0);
	}
	int F=flow(s,t),mx=-2;
	for (int i=1;i<=n;++i){
		de[i]->c=rev(de[i])->c=0;
		F-=flow(i,s);
		F+=flow(s,t);
		mx=max(mx,m-F-2);
		de[i]->c=2;
		if (mx>-2)
			break;
	}
	if (mx>-2)
		printf("No
");
	else
		printf("Yes
");
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14711232.html