【题解】2021牛客暑期多校第三场 C.Minimum grid

2021牛客暑期多校第三场Minimum grid

题意

给定一个\(n\times n\)的方阵,有\(m\)个位置需要填数,且每一行的数的最大值为\(b_i\),每一列数的最大值为\(c_i\),求方阵中所有数和的最小值。

\(1\le n\le 2\times 10^3,1\le m\le 8\times 10^5,1\le b_i,c_i\le k,1\le k\le 10^6\),且所有\(b_i,c_i\)中相同的值最多出现\(500\)次。

题解

填入的数只需要是\(b_i,c_i\)\(0\)。从大到小一次考虑每一种权值\(v\),将\(b_i=v\)的行和\(c_i=v\)得列提出来构成一个子阵,对行和列建点,对可以填数的位置的行列之间连边,则这种权值对答案的贡献为\(最小边覆盖*权值=(行数+列数-最大匹配)*权值\)。由于各个权值之间互不干扰,故不需要对每个权值分别建图,直接建一个图后跑二分图匹配即可。

#include <bits/stdc++.h>
#define pb(x) emplace_back(x)
using namespace std;
using ll=long long ;
const int N=2005;
int match[N],vis[N];
vector<int> g[N];
int n,m,k,a[N],b[N];
ll ans=0;
int dfs(int u){
	for(auto v:g[u])if(!vis[v]){
		vis[v]=1;
		if(!match[v]||dfs(match[v])){
			match[v]=u;return 1;
		}
	}
	return 0;
}
void f1(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++){
		cin>>a[i];ans+=a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];ans+=b[i];
	}
	while(m--){
		int x,y;cin>>x>>y;
		if(a[x]==b[y]){g[x].pb(y);}
	}
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		dfs(i);
	}
	for(int i=1;i<=n;i++){
		if(match[i])ans-=b[i];
	}
	cout<<ans;
} 
int main(){
	f1();
	return 0;
}
posted @ 2021-07-26 14:57  bobh  阅读(75)  评论(2编辑  收藏  举报