CH#17C 舞動的夜晚 和 BZOJ2140 穩定婚姻 和 HAOI2017 新型城市化


描述

L公司和H公司舉辦了一次聯誼晚會。晚會上,L公司的N位員工和H公司的M位員工打算進行一場交際舞。在這些領導中,一些L公司的員工和H公司的員工之間是互相認識的,這樣的認識關系一共有T對。舞會上,每位員工會嘗試選擇一名Ta認識的對方公司的員工作為舞伴,並且每位員工至多跳一支舞。完成的交際舞的數量越多,晚會的氣氛就越熱烈。顧及到晚會的氣氛,員工們希望知道,哪些員工之間如果進行了交際舞,就會使整場晚會能夠完成的交際舞的最大數量減小。

輸入格式

第一行三個整數N、M、T。
接下來T行每行兩個整數x、y,表示L公司的員工x和H公司的員工y互相認識。

輸出格式

第一行一個整數cnt,表示進行了交際舞后會使整場晚會能夠完成的交際舞的最大數量減小的員工有多少對。
第二行cnt個整數,升序輸出這樣的一hunyinhunyin對員工的認識關系的編號(他們的認識關系是在輸入數據中讀入的第幾條認識關系)。如果cnt=0,輸出一個空行。

樣例輸入

3 3 6
1 1
2 1
2 2
3 1
3 2
3 3

樣例輸出

3
2 4 5

數據范圍與約定

  • 對於50%的數據,1<=N,M<=100,1<=T<=1000。
  • 對於100%的數據,1<=N,M<=10000,1<=T<=100000,1<=x<=N,1<=y<=M。

題解

二分圖的不可行邊——最大流+Tarjan

對於50%的數據,先求出最大匹配的值,然后枚舉每一條邊,連上之后重求最大匹配,看答案是否為原最大匹配-1。求最大匹配用匈牙利算法。

對於100%的數據,先用Dinic求任意一組最大匹配,然后建一張新圖:

匹配邊(i,j) j到i連邊
非匹配邊 (i,j) i到j連邊
匹配的左點i (i,S)
不匹配的左點i (S,i)
匹配的右點j (T,j)
不匹配的右點j (j,T)

然后用Tarjan求強連通分量
(i,j)是可行邊的條件:
(i,j)是匹配邊 或者 i,j在同一個scc里

那么總邊數減去可行邊數就是不可行邊數,即答案。

注意這個新圖要包含源和匯,不能只在二分圖兩部之間連邊,除非原最大匹配是一個完備匹配。

證明過程,寫在書上了,很詳細。

時間復雜度\(O(E\sqrt{N+M})\)


注意那個源匯點連邊的時候,因為初始tot=1,所以枚舉的是正邊。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=2e4+2,M=3e5;
int head[N],ver[M],edge[M],next[M],d[N],e[M];
int n,m,p,s,t,tot=1,maxflow,ans;
il void add(int x,int y,int z){
	ver[++tot]=y,edge[tot]=z,::next[tot]=head[x],head[x]=tot;
	ver[++tot]=x,edge[tot]=0,::next[tot]=head[y],head[y]=tot;
}
bool bfs(){
	fill(d+s,d+t+1,0),d[s]=1;
	queue<int> q;q.push(s);
	for(int x;q.size();){
		x=q.front(),q.pop();
		for(int i=head[x],y;i;i=::next[i])
			if(edge[i]&&!d[y=ver[i]]){
				d[y]=d[x]+1,q.push(y);
				if(y==t) return 1;
			}
	}
	return 0;
}
int dinic(int x,int flow){
	if(x==t) return flow;
	int rest=flow;
	for(int i=head[x],delta;i&&rest;i=::next[i])
		if(edge[i]&&d[ver[i]]==d[x]+1){
			delta=dinic(ver[i],min(rest,edge[i]));
			if(!delta) d[ver[i]]=0;
			edge[i]-=delta,edge[i^1]+=delta;
			rest-=delta;
		}
	return flow-rest;
}

vector<int> a[N];
int c[N],sta[N],ins[N],dfn[N],low[N];
int scc,st,num;
il void add2(int x,int y){
	a[x].push_back(y);
}
void tarjan(int x){
	dfn[x]=low[x]=++num;
	sta[++st]=x,ins[x]=1;
	for(int i=0,y;i<a[x].size();++i){
		if(!dfn[y=a[x][i]]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		++scc;int y;
		do y=sta[st--],ins[y]=0,c[y]=scc; while(y!=x);
	}
}

int main(){
//	freopen("CH#17C.in","r",stdin);
	read(n),read(m),read(p),t=n+m+1;
	for(int i=1;i<=n;++i) add(s,i,1);
	for(int i=1;i<=m;++i) add(i+n,t,1);
	for(int i=1,x,y;i<=p;++i){
		read(x),read(y);
		add(x,n+y,1),e[i]=tot;
	}
	while(bfs())
		for(int delta;delta=dinic(s,1e9);) maxflow+=delta;
	for(int i=1;i<=p;++i)
		edge[e[i]]?add2(ver[e[i]^1],ver[e[i]]):add2(ver[e[i]],ver[e[i]^1]);
	for(int i=1;i<=n;++i) // notice: tot=1
		edge[2*i]?add2(s,i):add2(i,s);
	for(int i=1;i<=m;++i)
		edge[2*(n+i)]?add2(n+i,t):add2(t,n+i);
	for(int i=1;i<=t;++i)
		if(!dfn[i]) tarjan(i);
	ans=p;
	for(int i=1;i<=p;++i)
		ans-=edge[e[i]]||c[ver[e[i]]]==c[ver[e[i]^1]];
	printf("%d\n",ans);
	for(int i=1;i<=p;++i)
		if(!edge[e[i]]&&c[ver[e[i]]]!=c[ver[e[i]^1]]) printf("%d ",i);
	puts("");
	return 0;
}

BZOJ2140穩定婚姻

n對夫妻Bi和Gi。若某男Bi與某女Gj曾經交往過,他們有私奔的可能性。不妨設Bi和Gj舊情復燃,進而Bj會聯系上了他的初戀情人Gk,以此遞推。若在Bi和Gi離婚的前提下,這2n個人最終依然能夠結合成n對情侶,那么我們稱婚姻i為不安全的,否則婚姻i就是安全的。問n對夫妻的婚姻分別是安全的嗎?

題解

https://www.cnblogs.com/iiyiyi/p/5876168.html

第一反應是匈牙利算法,但是太過於暴力了,過不了。

我們把夫妻中女方連向男方,舊情中男方連向女方。可以得出結論:如果該有向圖的強連通分量中,夫妻雙方在同一個強連通分量里,那么他們的婚姻是不安全的,否則他們的婚姻是安全的。

為什么呢?如果在同一個強連通分量中,顯然可以連出一個增廣路,相當於匈牙利算法可以跑,那么必定是能形成新的n對情侶的。

如果不在一個強連通分量中,可以理解為匈牙利算法不能調整了(具體原因見匈牙利算法),那么必定不能形成新的n對情侶。

為什么在一個SCC中就不穩定呢?

考慮把Bi,Gi所在的環找一個出來,所有匹配沿着環轉一條邊,那么得到的也是一個合法的匹配。

不在一個SCC中顯然就不能這么做。

CO int N=8e4+10;
vector<int> to[N];
map<string,int> name;
int stk[N],ins[N],top;
int pos[N],low[N],dfn;
int col[N],idx;

void tarjan(int u){
	stk[++top]=u,ins[u]=1;
	pos[u]=low[u]=++dfn;
	for(int v:to[u]){
		if(!pos[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v]==1) low[u]=min(low[u],pos[v]);
	}
	if(low[u]==pos[u]){
		++idx;
		do col[stk[top]]=idx,ins[stk[top]]=0;
		while(stk[top--]!=u);
	}
}
int main(){
	int n=read<int>();
	for(int i=1;i<=n;++i){
		static char wife[15],husband[15];
		scanf("%s%s",wife,husband);
		name[wife]=i,name[husband]=i+n;
		to[i].push_back(i+n);
	}
	int m=read<int>();
	for(int i=1;i<=m;++i){
		static char girl[15],boy[15];
		scanf("%s%s",girl,boy);
		to[name[boy]].push_back(name[girl]);
	}
	for(int i=1;i<=2*n;++i)if(!pos[i]) tarjan(i);
	for(int i=1;i<=n;++i) puts(col[i]==col[i+n]?"Unsafe":"Safe");
	return 0;
}

HAOI2017 新型城市化

Anihc 國有 \(n\) 座城市,城市之間存在着一些貿易合作關系,如果城市 \(x\) 和城市 \(y\) 之間存在貿易協定,那么城市 \(x\) 和城市 \(y\) 則是一對貿易伙伴(注意:\((x, y)\)\((y, x)\) 是同一對城市)。

為了實現新型城市化,實現統籌城鄉一體化以及發揮城市群輻射與帶動作用,Anihc 國決定規划新型城市關系。一些城市能夠被稱為城市群的條件是:這些城市兩兩都是貿易伙伴。由於 Anihc 國之前也一直很重視城市關系建設,所以可以保證在目前已存在的貿易合作關系的情況下,Anihc 的 \(n\) 座城市可以恰好划分為不超過兩個城市群。

為了建設新型城市關系,Anihc 國想要選出兩個之前並不是貿易伙伴的城市,使這兩個城市稱為貿易伙伴,並且要求在這兩個城市稱為貿易伙伴之后,最大城市群的大小至少比他們稱為貿易伙伴之前的最大城市群的大小增加 \(1\)

Anihc 國需要在下一次會議上討論擴大建設新型城市關系的問題,所以需要請你求出在哪些城市之間建立貿易伙伴關系可以使得這個條件成立,即建立此關系前后的最大城市群的大小至少相差 \(1\)

對於所有的數據保證:\(n \le 10000, 0 \le m \le \min(150000, \frac{ n(n-1) }{2}), 1 \le x, y \le n\),保證輸入的城市關系中不會出現 \((x, x)\) 這樣的關系,同一對城市也不會出現兩次(無重邊,無自環)。

題解

https://jklover.hs-blog.cf/2020/06/03/Loj-2276-新型城市化/

二分圖最大匹配的必須邊.

首先可以在反圖上考慮這個問題(即將邊集替換為其補集).

問題變為給出一張二分圖,詢問刪掉哪些邊后最大匹配會減少,即哪些邊是最大匹配的必須邊.

首先用最大流跑出一個最大匹配,僅保留未滿流的邊做 tarjan,將每個點所屬的 SCC 求出.

\((u,v)\) 是最大匹配的可行邊,當且僅當它滿流,或 \(u,v\) 在相同的 SCC 中.

證明:若其滿流,顯然是可行邊,若在相同的 SCC 中,則可以替換掉原來的一條匹配邊.

\((u,v)\) 是最大匹配的必須邊,當且僅當它滿流,且 \(u,v\) 在不同的 SCC 中.

證明:若其不滿流,顯然不是必須邊,若在相同的 SCC 中,則可以被其他邊替換掉.

CO int N=1e4+10,inf=1e9;
vector<int> mat[N];
int col[N];

void paint(int x){
	for(int y:mat[x])if(!col[y])
		col[y]=3-col[x],paint(y);
}

int S,T;
struct edge {int y,c,a;};
vector<edge> to[N];
int dis[N];

IN void link(int x,int y,int c){
	to[x].push_back({y,c}),to[y].push_back({x,0});
	to[x].back().a=to[y].size()-1,to[y].back().a=to[x].size()-1;
}
bool bfs(){
	fill(dis+1,dis+T+1,inf),dis[S]=0;
	deque<int> que={S};
	while(que.size()){
		int x=que.front();que.pop_front();
		for(CO edge&e:to[x])if(e.c and dis[e.y]==inf)
			dis[e.y]=dis[x]+1,que.push_back(e.y);
	}
	return dis[T]<inf;
}
int dfs(int x,int lim){
	if(x==T) return lim;
	int rest=lim;
	for(edge&e:to[x])if(e.c and dis[e.y]==dis[x]+1){
		int delta=dfs(e.y,min(rest,e.c));
		if(delta==0) {dis[e.y]=inf; continue;}
		rest-=delta,e.c-=delta,to[e.y][e.a].c+=delta;
	}
	return lim-rest;
}

int pos[N],low[N],tim;
int stk[N],ins[N],top;
int scc[N],idx;

void tarjan(int x){
	pos[x]=low[x]=++tim;
	stk[++top]=x,ins[x]=1;
	for(CO edge&e:to[x])if(e.c){
		if(!pos[e.y]){
			tarjan(e.y);
			low[x]=min(low[x],low[e.y]);
		}
		else if(ins[e.y]) low[x]=min(low[x],pos[e.y]);
	}
	if(low[x]==pos[x]){
		++idx;
		do{
			int u=stk[top];
			scc[u]=idx,ins[u]=0;
		}while(stk[top--]!=x);
	}
}

int main(){
	int n=read<int>();
	S=n+1,T=n+2;
	for(int m=read<int>();m--;){
		int x=read<int>(),y=read<int>();
		mat[x].push_back(y),mat[y].push_back(x);
	}
	for(int i=1;i<=n;++i)if(!col[i]) col[i]=1,paint(i);
	for(int i=1;i<=n;++i){
		if(col[i]==1){
			link(S,i,1);
			for(int j:mat[i]) link(i,j,1);
		}
		else link(i,T,1);
	}
	while(bfs()) dfs(S,inf);
	for(int i=1;i<=T;++i)if(!pos[i]) tarjan(i);
	vector<pair<int,int> > ans;
	for(int x=1;x<=n;++x)if(col[x]==1)
		for(CO edge&e:to[x])if(e.y<=n and e.c==0 and scc[x]!=scc[e.y])
			ans.push_back({min(x,e.y),max(x,e.y)});
	sort(ans.begin(),ans.end());
	printf("%zd\n",ans.size());
	for(CO pair<int,int>&p:ans) printf("%d %d\n",p.first,p.second);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM