[补题记录] 2023 Hubei Provincial Collegiate Programming Contest(M、C、H、J、F)

URL:Dashboard - 2023 Hubei Provincial Collegiate Programming Contest - Codeforces

个人认为这几个是铜牌题。

目录

M

1.Problem/题意

2.Thought/思路

3.Code/代码

C

1.Problem/题意

2.Thought/思路

3.Code/代码

H

1.Problem/题意

2.Thought/思路

3.Code/代码

J

1.Problem/题意

2.Thought/思路

3.Code/代码

F

1.Problem/题意

2.Thought/思路

3.Code/代码


M

1.Problem/题意

有三种队伍参加比赛,A类要花0、B类要花1000、C类要花2500,给出队伍数量x、主办方收到钱y。问任意一种队伍数量的组合。

2.Thought/思路

因为y最大是1e9,因此可以枚举B类队伍的数量。

这个题的坑点在于需要判断剩余的钱,和剩余的队伍数量之间的关系。

3.Code/代码


#include "bits/stdc++.h"

#define int long long

signed main() {
	int x, y; std::cin >> x >> y;

	bool flag = false;
	int a = 0, b = 0, c = 0;
	for (int i = 0; i <= x; ++ i) { // b's number
		b = i;
		int left = y - 1000 * b;
		if (left < 0) continue;

		if (left > (x - b) * 2500) continue;
		if (left == (x - b) * 2500) {
			c = left / 2500;
			a = 0;
			flag = true;
			break;
		}
		if (left < (x - b) * 2500) {
			if (left % 2500 == 0) {
				c = left / 2500;
				a = x - b - c;
				flag = true;
				break;
			}
		}
	}

	if (flag) std::cout << a << " " << b << " " << c;
	else std::cout << -1;
}

C

1.Problem/题意

给一个N x M的矩阵,一开始上面全是白点,如果一个白点的上下左右四个位置至少存在两个黑点,那么这个白点可以自行转换为黑点。

问初始状态下最少放几个黑点能将矩阵全变黑。

2.Thought/思路

考虑n = m的特殊情况,只需要沿着对角线放黑点就是最少的情况。

那么当给它多增加两列之后,比如 5 X 5 变为 5 X 7,我们发现,只要在新增加的第一列上随意放一个黑点,就能全部变黑。

而如果是一个 5 X 8的矩阵,只要隔一个位置放黑点,就能全部变黑。

3.Code/代码

#include "bits/stdc++.h"

signed main() {
	int n, m; std::cin >> n >> m;
	if (n == 0) std::cout << m / 2 + 1;
	else if (m == 0) std::cout << n / 2 + 1;
	else {
		int min = std::min(n, m), max = std::max(n, m);
		std::cout << min + (max - min + 1) / 2;
	}
}

H

1.Problem/题意

给出一个复杂图,有n个点、m条边,问\sum_{i = 1}^{n}\sum_{j = i}^{n} f(i, j)是多少。

其中 f(i, j) = (degi xor degj) * (degi | degj) * (degi & degj)。

2.Thought/思路

(1)我们发现最后要求的是一个东西的和,也就是说,如果有多个相同的 f(i, j),我们就可以想办法一次性解决。

(2)考虑到 f(i, j) 里其实是度数在做运算,那么对于同一对度数组合,如果分别知道两个度数的数量,不就可以一次解决了吗。我们将 f(i, j) 用 f(degi, degj) 替换,然后举个例子。

比如:对于度数组合 (3, 4),如果度数3的点有4个,度数4的点有5个,那么这 4 + 5 = 9 个点对答案的贡献为 

ans += f(3, 4) * cnt[3] * cnt[4]

(3)那么再考虑一下复杂度,m最大是1e6,也就是说最多有 sqrt(m) 种度数,显然可以通过两重for循环遍历度数组合。

3.Code/代码

#include "bits/stdc++.h"

#define int long long

const int mod = 998244353;

int n, m, deg[2000007], cnt[2000007];
struct node {
	int cnt;
	int deg;
};

std::vector <node> v;

int f(int di, int dj) {
	return (di ^ dj) * (di | dj) * (di & dj);
}

signed main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);

	std::cin >> n >> m;
	for (int i = 1; i <= m; ++ i) {
		int x, y; std::cin >> x >> y;
		deg[x] ++;
		deg[y] ++;
	}

	for (int i = 1; i <= n; ++ i) cnt[deg[i]] ++;

	for (int i = 1; i <= 2 * m; ++ i) 
		if (cnt[i] > 0) v.push_back({cnt[i], i});

	int ans = 0;
	for (int i = 0; i < v.size(); ++ i) {
		for (int j = i; j < v.size(); ++ j) {
			int di = v[i].deg, dj = v[j].deg;
			ans = (ans + ((f(di, dj) * v[i].cnt % mod) * v[j].cnt % mod)) % mod;
		}
	}
	std::cout << ans;
}

J

1.Problem/题意

小马有n块田需要耕作,每块田有资源ai。

假设它已经耕作了 [1, X] 并且现在是第 i 天,那么它可以选择在这天的早上开垦第 X + 1块田,或者不开垦;然后在这天的晚上,会得到已经开垦过的田的资源总和,问最少需要几天能把所有田都开垦完。

要求:

整个过程要时刻保证资源 >= 0;

2.Thought/思路

(1)注意题目可以选择是否耕作的条件是 [1, X],也就是说,第一天是必须要耕作的。

(2)所有的田都要经过,那么先考虑最简单的情况。一路耕作下去,假设某个时刻遇到了一个前缀和,使得资源变成了负数。显然这个时候我们应该选择在前面的某个前缀和为正数的位置停留一会,显然这个正数前缀和应该选最大的才满足贪心。而且维护这个最大值也很简单,每个位置对前缀和取大即可。

3.Code/代码

#include "bits/stdc++.h"

#define int long long

int n, a[100007], pre[100007];

signed main() {
	std::cin >> n;
	
	bool flag = true;
	for (int i = 1; i <= n; ++ i) {
		std::cin >> a[i];
		pre[i] = pre[i - 1] + a[i];
	}

	if (pre[n] < 0) flag = false;
	for (int i = 1; i <= n; ++ i) {
		if (a[i] < 0) 
			flag = false;
		else if (a[i] > 0) 
			break;
	}

	if (flag == false) {
		std::cout << -1;
		return 0;
	}

	int ans = 0, max = 0, res = 0;
	for (int i = 1; i <= n; ++ i) {
		max = std::max(max, pre[i]);
		ans ++;
		res += pre[i];
		if (res < 0) {
			int add = 0;
			if (std::abs(res) % max == 0) 
				add = std::abs(res) / max;
			else 
				add = std::abs(res) / max + 1;

			ans += add;
			res += add * max;
		}
	}

	std::cout << ans;
	
}

F

1.Problem/题意

题意是将马拉车的过程逆序,即给出回文半径,用a和b构造出原字符串。

2.Thought/思路

我们发现,当遇到 '|' 时,只会有两种情况:a[i] = 1 和 a[i]  > 1。

其中,等于1的情况,必然会使 | 的下一个位置与 | 的前一个位置的字符相反(即 a 与 b 相反)。

然后,大于1的情况,必然有左右两侧字符串回文,我们只需要复制 | 左边的第一个即可。

3.Code/代码

注意输入量较大。


#include "bits/stdc++.h"

int n, a[2000007];
char ans[2000007];

char reverse(char x) {
	if (x == 'a') return 'b';
	if (x == 'b') return 'a';
}

signed main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);
	std::cin >> n;

	for (int i = 0; i < 2 * n + 2; ++ i) std::cin >> a[i];

	ans[0] = '&';
	ans[1] = '|';
	ans[2] = 'a';
	for (int i = 3; i < 2 * n + 2; ++ i) {
		if (ans[i] != 0) continue;
		if (i & 1) { // |
			ans[i] = '|';
			if (a[i] == 1) ans[i + 1] = reverse(ans[i - 1]);
			else ans[i + 1] = ans[i - 1];
		}
	}
 
	for (int i = 2; i < 2 * n + 2; i += 2) std::cout << ans[i];
}	

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值