2021牛客暑期多校3 BCEFJ题解

本文探讨了两种经典算法问题的解决方案:一是通过构造图并寻找最小生成树解决矩阵涂色问题,二是利用最大匹配策略解决矩阵元素排列优化问题。涉及算法包括Prim算法、Kruskal算法和匈牙利算法。通过对每个问题的思路分析和代码实现,展示了如何在实际问题中应用图论知识。
摘要由CSDN通过智能技术生成

B.Black and white
思维题, 只要想出来就好做了.
题目大意:
把一个矩阵全涂黑, 问最少需要多少花费,对一个2*2的矩阵的时候,有三个被涂黑了,那么第四个就黑了.每个元素的值就是涂黑的花费.
思路:
我们把行和列都看成一个点, 每个格子看成点之间的连线, 然后就得到了一个图. 在图上求最小生成树, 使得整个图能联通. 那就是所求值.
代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5050;
int fa[N * 2];
ll n, m, a, b, c, d, p;

int find(int x) {
	if (x == fa[x]) {
		return x;
	} else {
		return fa[x] = find(fa[x]);
	}
}

struct vv {
    int u, v, w;
	bool operator < (const vv h) const {
		return h.w < w;
	}
};
priority_queue<vv> pp;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m >> a >> b >> c >> d >> p;
	ll tmp = a;
    
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			tmp = (tmp * tmp * b + tmp * c + d) % p;
			pp.push({ i, j + (int)n, (int)tmp});
		}
	}
    
	for (int i = 1; i <= n + m; i++) fa[i] = i;
	ll ans = 0, cot = 0;
	while (!pp.empty()) {
		vv tm1 = pp.top();
		pp.pop();
		//         cout << tm1.u << " " << tm1.v << " " << tm1.w << endl;
		int fu = find(tm1.u), fv = find(tm1.v);
		if (fu == fv) continue;
		ans += tm1.w;
		cot++;
        fa[fu] = fv;
		if (cot == n + m - 1) break;
	}
	cout << ans << endl;
}

C. Minimum grid
最大匹配
题目大意:
给出每一行,每一列的最大值,然后给出能存放点的坐标, 然后求最后最小的和.
思路:
对于某个值, 我们要想最小化值, 就是尽量让相同值放在同一个地方, 所以我们就让相同的值作为点, 然后能用的格子作为边. 找最大匹配, 也就是尽量让相同的点匹配.
代码:

#pragma GCC optimize(3)
#include<bits/stdc++.h>

using namespace std;
using ll = long long;

#define ctx cout << "xxxxxxx" << endl

const int K = 1e6 + 10;
const int N = 1e4 + 10;

vector<int> row[K], col[K];
bool mp[N][N];
int match[N], used[N];
vector<int> tmp[N];

ll n, m, k;


bool dfs(int pos)
{
	for (auto i : tmp[pos])
	{
		if (used[i]) continue;
		used[i] = 1;
		if (!match[i] || dfs(match[i]))
		{
			match[i] = pos;
			return 1;
		}

	}
	return 0;
}

int main() {
	cin >> n >> m >> k;
	int x;
	for (int i = 1; i <= n; i++) {
		cin >> x;
		row[x].push_back(i);
	}
	for (int i = 1; i <= n; i++) {
		cin >> x;
		col[x].push_back(i + n);
	}
	for (int i = 1; i <= m; i++) {
		int y;
		cin >> x >> y;
		// 		if(x <= n && y <= n) 
		mp[x][y] = 1;
	}
	ll ans = 0;
	for (int i = k; i >= 1; i--) {
		if (row[i].size() == 0 && col[i].size() == 0) continue;
		//		vector<int> tmp[n+5];
		for (int i = 1; i <= n; i++) tmp[i].clear();
		for (auto r : row[i]) {
			for (auto c : col[i]) {
				if (mp[r][c - n]) tmp[r].push_back(c);
			}
		}
		memset(match, 0, sizeof(match));
		ll res = 0;
		for (int i = 1; i <= n; i++) {
			memset(used, 0, sizeof(used));
			if (dfs(i)) res++;
		}
        ll maxx = row[i].size() + col[i].size() - res;
        ans += maxx*i;
	}

	cout << ans << endl;
}


E. xy = k*(xx + yy), 1 <= x <= y <= n, 求满足的(x, y)对.
思路:
先暴力来一发, 打表找规律. 发现(x, x^3)一定满足, 后面出现了一些跳跃, 表现是y = pre_y * x ^ 2 - pre_x, x = pre_y;类似这样.
存住表, 然后排序, 二分找.
代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

vector<ll> num;

void init(){
	for(ll i = 2; i <= (1e6); i++){
		ll tm1 = i*i*i, tm2 = i;
		while(tm1 <= (1e18)){
			num.push_back(tm1);
			if(tm1 > (1e18+i)/(i*i)) break;
			ll tm3 = tm1*i*i - tm2;
            if(tm3 > 1e18) break;
			tm2 = tm1;
            tm1 = tm3;
		}
	}
	sort(num.begin(), num.end());
}

int main(){
	num.push_back(1);
	init();
	ll t;
	cin >> t;
//     for(int i = 0; i <= 100; i++) cout << num[i] << endl;
	while(t--){
		ll n;
		cin >> n;
		ll ans = lower_bound(num.begin(), num.end(), n+1) - num.begin();
		cout << ans << endl;
	}
}

F. 24 dian
暴力
题目:
四个数,加上 + , - , *, /, ‘(’ , ‘)’ 使之能得到m。必须包含分数。
思路:
暴力dfs,每次找两个数四则运算,一直到全部可能用完。
代码不会… 朋友给的

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f

const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int a[maxn], mp[14][14][14][14];
int n, m;
int cnt = 0;
vector<int>nows(4);

void dfs(vector<double>x, bool flag) {
    if (x.size() == 1) {//计算结束得到一个值
        if (abs(x[0] - m) < eps) {//该值是m
            if (flag) {//如果出现过小数
                if (mp[nows[0]][nows[1]][nows[2]][nows[3]] == 0)cnt++;//该数未被找到过则cnt++
                mp[nows[0]][nows[1]][nows[2]][nows[3]]++;//记录该值被发现过一次
            }
            else {//没有出现过小数
                if (mp[nows[0]][nows[1]][nows[2]][nows[3]] > 0)cnt--;//如果发现过该值是小数 cnt--
                mp[nows[0]][nows[1]][nows[2]][nows[3]] = -INF;//标记该值可以没有出现过小数达到 
            }
        }
        return;
    }
    for (int i = 0;i < x.size();i++) {
        vector<double>tmp1 = x;
        double b;    double a = x[i];//a取x的第i位

        for (int j = i;j < tmp1.size() - 1;j++)//把x第i位移除得到tmp1 
            tmp1[j] = tmp1[j + 1];
        tmp1.pop_back();

        for (int j = 0;j < tmp1.size();j++) {
            vector<double>tmp = tmp1;
            b = tmp[j];//b取x的第j位
            for (int k = j;k < tmp.size() - 1;k++)//把tem1第j位移除得到tmp 
                tmp[k] = tmp[k + 1];
            tmp.pop_back();
			//把a和b的计算结果放回数组 继续进行计算 
            tmp.push_back(a + b); 
            dfs(tmp, flag);
            tmp.pop_back();

            tmp.push_back(a - b);
            dfs(tmp, flag);
            tmp.pop_back();

            tmp.push_back(b - a);
            dfs(tmp, flag);
            tmp.pop_back();

            tmp.push_back(a * b);
            dfs(tmp, flag);
            tmp.pop_back();

            if (abs(b - 0) > eps) {
                tmp.push_back(a / b);
                if (abs(round(a / b) - a / b) > eps) {
                    // cout << a << ' ' << b << endl;
                    dfs(tmp, 1);
                }
                else
                    dfs(tmp, flag);
                tmp.pop_back();
            }

            if (abs(a - 0) > eps) {//a不是0就做“除以0 ”这个操作 
                tmp.push_back(b / a);
                if (abs(round(b / a) - b / a) > eps) {//四舍五入的值和不四舍五入的值有差别则是小数 
                    // cout << b << ' ' << a << endl;
                    dfs(tmp, 1);
                }
                else//否则是整数 
                    dfs(tmp, flag);
                tmp.pop_back();
            }
        }
    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> n >> m;
    if (n <= 3)
    {
        cout << 0;
        return 0;
    }
    for (int i = 1;i <= 13;i++)
        for (int j = i;j <= 13;j++)
            for (int k = j;k <= 13;k++)
                for (int l = k;l <= 13;l++) {
                    nows[0] = i, nows[1] = j, nows[2] = k, nows[3] = l;
                    vector<double> tmp(4);
                    tmp[0] = i, tmp[1] = j, tmp[2] = k, tmp[3] = l;
                    dfs(tmp, 0);
                }
    cout << cnt << endl;
    for (int i = 1;i <= 13;i++)
        for (int j = i;j <= 13;j++)
            for (int k = j;k <= 13;k++)
                for (int l = k;l <= 13;l++)
                    if (mp[i][j][k][l] > 0)
                        cout << i << ' ' << j << ' ' << k << ' ' << l << endl;
}

J. Counting Triangles
思维题。
题目大意:
用给的程序生成边的颜色, 求颜色相同的三角形。
思路:
所有的可能减去不能的,就是答案。我们对每个点统计黑色和白色,那么这些边组成的三角形一定不能成。就是hei * bai。然后由于每个点都会遍历两次,所以最后结果除2.
代码

#include<bits/stdc++.h>


namespace GenHelper
{
    unsigned z1,z2,z3,z4,b,u;
    unsigned get()
    {
        b=((z1<<6)^z1)>>13;
        z1=((z1&4294967294U)<<18)^b;
        b=((z2<<2)^z2)>>27;
        z2=((z2&4294967288U)<<2)^b;
        b=((z3<<13)^z3)>>21;
        z3=((z3&4294967280U)<<7)^b;
        b=((z4<<3)^z4)>>12;
        z4=((z4&4294967168U)<<13)^b;
        return (z1^z2^z3^z4);
    }
    bool read() {
      while (!u) u = get();
      bool res = u & 1;
      u >>= 1; return res;
    }
    void srand(int x)
    {
        z1=x;
        z2=(~x)^0x233333333U;
        z3=x^0x1234598766U;
        z4=(~x)+51;
      	u = 0;
    }
}
using namespace GenHelper;
using namespace std;
using ll = long long;
bool edge[8005][8005];
int n, seed;

void solve();


int main() {
 
  cin >> n >> seed;
  srand(seed);
  for (int i = 0; i < n; i++)
    	for (int j = i + 1; j < n; j++)
        	edge[j][i] = edge[i][j] = read();
    ll ans = 0, tmp = 0;
    for(int i = 0; i < n; i++){
        ll hei = 0, bai = 0;
        for(int j = 0; j < n; j++){
            if(j == i) continue;
            if(edge[i][j]) hei++;
            else bai++;
        }
        tmp += hei * bai;
    }
    ans = (ll)n*(n-1)*(n-2) / (3*2) - tmp/2;
    cout << ans << endl;
 	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值