LG15645 [ICPC 2022 Tehran R] Network Topology in Hezardastan 题解

LG15645 [ICPC 2022 Tehran R] Network Topology in Hezardastan 题解

\(m\) 个终端,\(n\) 个服务器(\(m\le n\))。\(m\times n\) 的 0/1 矩阵表示连接终端与服务器是否连接。称大小为 \(m\) 的服务器子集 \(S\)可管理的,则存在双射使每个终端连到不同服务器(即服务器子集可以终端完美匹配)。若所有大小为 \(m\) 的子集均可管理,输出 1;否则输出 0 及任意一个不可管理的大小为 \(m\) 的子集(服务器编号)。\(m\leq 150,n\leq 400,m\leq n\)

是一个二分图,容易想到 Hall 定理,设 \(T\) 为一个终端的非空子集\(N(T)\) 为所有与 \(T\) 相邻顶点集合,则

\[\forall T,|N(T)\cap S|\geq |T| \]

\(|N(T)\cap S|\)\(S\) 中与 \(T\) 相连的集合大小(即节点个数),要最小化这个值才能使条件不满足。要最小化的话,需要 \(S\) 选取时尽量不包含 \(N(T)\) 中的节点。记服务器集合全集为 \(U\)

  • \(|U\setminus N(T)|=n-|N(T)|\geq m\),则取 \(S\subseteq U\setminus N(T)\) 即可构造出 \(|N(T)\cap S|=0\),说明存在不可管理服务器子集。

  • 否则 \(S\) 包含 \(|U\setminus N(T)|=n-|N(T)|\) 个不属于 \(N(T)\) 的元素,\(|N(T)\cap S|=m-(n-|N(T)|)\geq |T|\)

综上可以得到子集均可管理的充要条件

\[\forall T, |N(T)|-|T|\geq n-m \]

\(F(T)=|N(T)|-|T|\),现在要求 \(F(T)\) 的最小值以及与 \(n-m\) 的关系。

如果构造这样一个网络流模型

源点 \(s\) 向每个终端(左侧点)连一条流量为 \(1\) 的边,每个终端向相连的服务器(右侧点)连一条流量为 \(+\infty\) 的边,每个服务器向汇点连一条流量为 \(1\) 的边。

则求这个模型的最小割,中间终端与服务器的边不会属于最小割,将不属于左侧最小割的点视作 \(T\),则右侧需要属于最小割的点为 \(N(T)\),最小割为 \(|N(T)|+(m-|T|)=F(T)+m\),则需要判断最小割与 \(n\) 的关系。

最小割等于最大流,这个结论应该不必多说了

由于我们没要求 \(T\) 非空,除非原图的匹配就小于 \(m\)(此时不管怎么选都不会有可管理服务器),那么最小割就会等于 \(m\),对应 \(T\) 为空集,左侧边全部被割掉的情况。现在还需要求非空 \(T\) 的最小割。

这样很好做,每次给一条源点 \(s\) 向终端(左侧点)的边流量改为 \(\infty\),则这条边就不可能属于最小割,那么 \(T\) 中必须包含这个点,此时求得的最小割对应的集合 \(T\) 就是非空的。

如果其中一个最小割的值 \(<n\),那么 \(F(T)=最小割-m<n-m\),就存在不可管理的服务器子集。现在构造出这个子集,左侧不属于最小割的点集即为 \(T\),计算出 \(T\) 的邻集 \(N(T)\),优先选择 \(U\setminus N(T)\) 中的点,接着选择 \(N(T)\) 中的点直到 \(m\) 个,根据一开始的证明,选择的 \(N(T)\) 中的点的个数是小于 \(|T|\) 的,这也意味着单独 \(T\) 这个终端子集就一定无法与服务器子集 \(S\) 完美匹配,于是整个终端集合也无法与服务器子集 \(S\) 完美匹配,找到了一组解。

Dinic 算法求二分图匹配单次复杂度 \(O(E\sqrt V)=O(nm\sqrt{n+m})\),总复杂度 \(O(m^2n\sqrt{n+m})\),可以通过此题。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int,int>ttfa;
const int N=500005;
const int INF=0x3f3f3f3f;
const ll llINF=0x3f3f3f3f3f3f3f3f;

struct net_flow{
	struct chainstar{
		int nxt,v;ll w;
	}edge[N];int head[N],now[N],ecnt=1;
	int S,T,n,dep[N];
	inline void init(int SS,int TT,int nn){
		S=SS,T=TT,n=nn,ecnt=1;
		for(int i=0;i<=n;++i)head[i]=now[i]=0;
	}
	inline void addline(int u,int v,ll w){
		edge[++ecnt].nxt=head[u];
		head[u]=ecnt;
		edge[ecnt].v=v;
		edge[ecnt].w=w;
	}
	inline int addedge(int u,int v,ll w){
		addline(u,v,w);
		addline(v,u,0ll);
		return ecnt-1;
	}
	queue<int>q;
	inline bool bfs(){
		for(int i=0;i<=n;++i)dep[i]=0;
		dep[S]=1;q.push(S);
		while(!q.empty()){
			int u=q.front();q.pop();
			for(int i=head[u];i;i=edge[i].nxt){
				int v=edge[i].v;
				if(edge[i].w>0&&!dep[v]){
					dep[v]=dep[u]+1;
					q.push(v);
				}
			}
		}
		return dep[T];
	}
	ll dfs(int u,ll f){
		if(u==T)return f;
		ll tmp=0;
		for(int i=now[u];i&&f;i=edge[i].nxt,now[u]=i){
			int v=edge[i].v;
			if(edge[i].w>0&&dep[v]==dep[u]+1){
				ll ttf=dfs(v,min(f,edge[i].w));
				if(ttf==0)dep[v]=n+2;
				edge[i].w-=ttf;
				edge[i^1].w+=ttf;
				tmp+=ttf;f-=ttf;
			}
		}
		return tmp;
	}
	ll dinic(){
		ll sum=0ll;
		while(bfs()){
			for(int i=0;i<=n;++i)now[i]=head[i];
			while(ll tmp=dfs(S,llINF))sum+=tmp;
		}
		return sum;
	}
}flow;

int n,m;
int a[155][405];

void buildmap(){
	flow.init(n+m+1,n+m+2,n+m+2);
	for(int i=1;i<=m;++i){
		flow.addedge(flow.S,i,1);
	}
	for(int i=1;i<=n;++i){
		flow.addedge(m+i,flow.T,1);
	}
	for(int i=1;i<=m;++i){
		for(int j=1;j<=n;++j){
			if(a[i][j])
				flow.addedge(i,j+m,2*n);
		}
	}
}

int main(){
	scanf("%d%d",&m,&n);
	for(int i=1;i<=m;++i){
		for(int j=1;j<=n;++j){
			scanf("%d",&a[i][j]);
		}
	}
	buildmap();
	int sum=flow.dinic();
	if(sum<m){
		puts("0");
		for(int i=1;i<=m;++i){
			if(i!=1)printf(" ");
			printf("%d",i);
		}
		puts("");
		return 0;
	}
	for(int i=1;i<=m;++i){
		buildmap();
		flow.edge[i*2].w=2*n;
		sum=flow.dinic();
		if(sum<n){
			vector<int>lis,tag(n+1,0);
			for(int j=1;j<=m;++j){
				if(flow.edge[j*2].w>0){
					for(int k=1;k<=n;++k){
						if(a[j][k])tag[k]=1;
					}
				}
			}
			for(int k=1;k<=n;++k){
				if(!tag[k])lis.push_back(k);
			}
			for(int k=1;k<=n;++k){
				if(tag[k])lis.push_back(k);
			}
			while((int)lis.size()>m)lis.pop_back();

			sort(lis.begin(),lis.end());
			puts("0");
			for(int j=0;j<m;++j){
				if(j)printf(" ");
				printf("%d",lis[j]);
			}
			puts("");
			return 0;
		}
	}
	puts("1");
	return 0;
}

如果证明有错误请多多指正。

另外这个题如果你的写法和原始出题人不一样,大概是只能在洛谷上通过的,这里给洛谷点个赞。其他平台上的 spj 都有问题(包括但不限于答案集合不排序不行,答案集合是正确的仍判错)。

posted @ 2026-05-20 00:20  BigSmall_En  阅读(14)  评论(0)    收藏  举报