2021牛客多校3

这一场是把前两场的图论都出完了么

B Black and white[图论]

这道题曾经校赛出过一个很类似的,散播弗兰三三的花瓣啥的,当时学长给的标答是并查集,牛逼队友也快乐地用并查集排个序就过掉了这题

#include <cstdio>
#include <algorithm>

#define int long long

const int MAXN = 5e3 + 5;

int n, m, a, b, c, d, p, ans;

struct NODE
{
	int id;
	int val;
};

inline bool operator < (const NODE& lhs, const NODE& rhs)
{
	return lhs.val < rhs.val;
}

NODE A [MAXN * MAXN];

int fa [MAXN << 1]; // col: 1 ~ n, row: n + 1 ~ n + m

inline int find (int x)
{
	return fa [x] == x ? x : fa [x] = find (fa [x]);
}

inline bool merge (int x, int y)
{
	x = find (x);
	y = find (y);
	
	if (x != y)
		return fa [x] = y, true;
	
	return false;
}

inline bool merge (const NODE& node)
{
	int x = (node.id - 1) / m + 1;
	int y = (node.id - 1) % m + 1 + n;
	
	return merge (x, y);
}

signed main ()
{
	scanf ("%lld%lld%lld%lld%lld%lld%lld", &n, &m, &a, &b, &c, &d, &p);
	
	A [0].val = a;
	
	for (int i = 1; i <= n * m; i ++)
		A [i].val = (A [i - 1].val * A [i - 1].val * b + A [i - 1].val * c + d) % p, A [i].id = i;
	
	for (int i = 1; i <= n + m; i ++)
		fa [i] = i;
		
	std::sort (A + 1, A + n * m + 1);
	
	for (int i = 1; i <= n * m; i ++)
		if (merge (A [i]))
			ans += A [i].val;
			
	printf ("%lld\n", ans);
}

然后出题人表示这是一个套路题。行和列为点,把网格抽象成一个完全二分图,问题变为选权值最小的边使得二分图连通,此时方格的四个角在连三条边的情况下就连通了。
所以是一个最小生成树,完全图所以就prim了

#include<iostream>
#include<cstring>
#include<vector>
#define int long long 
using namespace std;
typedef long long ll;
const int N = 5e3+10;
const int INF = 0x3f3f3f3f;
int cost[N][N];
int A[N*N],dis[N+N],vis[N+N];
int n,m,a,b,c,d,mod;
ll prim(){
	memset(dis,0x3f,sizeof dis);
	ll ans = 0;
	dis[1] = 0;vis[1] = 1;
	int last=1;
	
	for(int i=1;i<n+m;i++){
		if(last <= n){
			for(int j=1;j<=m;j++){
				dis[j+n]=min(dis[j+n],cost[last][j]);
			}
		}
		if(last > n){
			for(int j=1;j<=n;j++) {
				dis[j]=min(dis[j],cost[j][last-n]);
			}
		}
		int minn = INF, pos = -1;
		for(int j=1;j<=n+m;j++){
			if((!vis[j]) && minn > dis[j]){
				pos = j;
				minn = dis[j];
			}
		}
		ans += minn; 
		last = pos;
		vis[pos] = 1;
	}
	return ans;
}
signed main() {
	cin>>n>>m>>a>>b>>c>>d>>mod;
	A[0]=a;
	for(int i=0;i<=n*m;i++)
		A[i+1]=(A[i]*A[i]*b+A[i]*c+d)%mod;
		
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++){
			cost[i][j] = A[m*(i-1)+j];
		}
	}
	cout<<prim();
}

C Minimum grid[图论]

先将所有能填的格子都填上,再考虑有哪些是重复了可以去掉的。

我们称 b i b_i bi c i c_i ci 为⾏限定数和列限定数,统称限定数.
考虑最⼤的那个限定数,不妨设为 x x x ;找到⾏、列限定数是 x x x 的那些⾏和列组成的⼦矩形.
显然,其他数都不能满⾜ x x x的要求.
于是我们把每⾏建⼀个点,每列建⼀个点,可以填数的位置建⼀条边,得到⼀个⼆分图;这个⼆分图的最⼤匹配就是我们能节省出的 x x x的个数.
这样把每个限定数的⼆分图都建出来跑⼀遍匹配,算⼀下贡献即可

然而实现起来很简单,在整个图构造的二分图里,只要最后在匹配里那么这个匹配对应的行就可以节约一个列,因此直接对行匹配,算贡献就行了

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 2e3+5;
vector<int> mp[N];
int b[N],c[N],n,m,k,link[N],vis[N];
int dfs(int x){
    for (auto v:mp[x]){
    	if(vis[v]) continue;
        vis[v] = 1;
        if (link[v] == 0 || dfs(link[v])){
            link[v] = x;
            return 1;  
        }
    }
    return 0;
}
int main() {
	ll ans = 0;
  	cin>>n>>m>>k;
  	for(int i=1;i<=n;i++){
  		cin>>b[i];
  		ans += b[i];
  	}
  	
  	for(int i=1;i<=n;i++){
  		cin>>c[i];
  		ans += c[i];
  	}
  	
  	for(int i=0,u,v;i<m;i++){
  		cin>>u>>v;
  		if(b[u] == c[v]){
  			mp[u].push_back(v);
  		}
  	}
  	
  	for(int i=1 ;i<= n; i++){
  		memset(vis,0,sizeof vis);
  		ans -= b[i]*dfs(i);
  	}
	cout<<ans<<endl;
 	return 0;
}

F 模拟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值