Codeforces Round 942 (Div. 2) 题解

A题: Contest Proposal

题目大意:

给你一个大小为 n 的数组 a 和数组 b,你可以进行下列操作若干次--在数组 a 的任意位置插入一个数字(要求插入后数组 a 还是升序的,每次插入一个数 a 就会弹出末尾的最大那个数)。

问:至少需要多少次操作才能使得 i\epsilon (1,n)中的每一个i, a_{i} \leq b_{i} 。

思路:

题目的意思是不是我们利用上更多的 a 中原来有的元素,这样答案才会更小?

对于一个 a_{i} ,如果能找到一个 b_{j} (j\epsilon (i, n) 是大于等于其的,那就可以说它是可以利用上的,这样就能少插入一个。

用了双指针,因为要比较。

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int tt; cin >> tt;
	while (tt -- ) {
		int n; cin >> n;
		vector<int> a(n), b(n);
		for (int i = 0; i < n; i ++ ) cin >> a[i];
		for (int i = 0; i < n; i ++ ) cin >> b[i];
		int now = 0, ans = 0;
		for (int i = 0; i < n; i ++ ) {
			if (a[i] <= b[now]) {
				now += 1;
			}else {
				while (a[i] > b[now] && now < n) {
					ans += 1;
					now += 1;
				}
				if (now == n) break;
				now += 1;
				if (now == n) break;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

B题:Coin Games 

题目大意:

n枚硬币围成一个圈,每个硬币有面朝上和面朝下两种状态。每当轮到一名玩家时,玩家取出一枚面朝上的硬币,并且将其两旁的硬币改变面的朝向,当没有硬币可以取时游戏结束(没有面朝上的或者全取完了),轮到的玩家失败。问先手的胜负情况。

思路:

有这么几种情况(取法)

DUD -->操作后U的数量会+1
DUU -->操作后U的数量会-1
UUU -->操作后U的数量会-3

所以,在操作的过程中U的数量是奇偶变化的,所以我们只要遍历一遍字符串,如果U的数量是奇数,那么先手在操作过程中遇到的状态都是奇数,也就是必胜。反之,一定必败。

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int tt; cin >> tt;
	while (tt -- ) {
		int n; cin >> n;
		string s; cin >> s;
		int cnt = 0;
		for (auto c : s) {
			if (c == 'U') {
				cnt += 1;
			}
		}
		if (cnt & 1) cout << "YES\n";
		else cout << "NO\n";
	}
	return 0;
}

C题:  Permutation Counting

题目大意:

你有 n纸牌,每纸牌上标有(1,n)的数字。

标有 i 数字的纸牌的数量由数组 a_{i} 表示。

要求你使用你得到的纸牌,构造出一个数组,数组中每个连续的,大小为 n 的子数组(数组中的元素对应(1,n)的数字每个都出现一次)就能使你的得分加1,要求你输出你的最大化得分。

思路:

一:短板效应

给出这么一个例子。

5 0

0 999 999 999 999

首先你肯定会说,这是构不成[1, 2, 3, 4, 5]的,所以得分是0。

然后如果是

5 0

1 999 999 999 999

先不管答案,但是你的最大化得分一定受到了这个1的影响。

所以,如果可以,你会将你的coin(题目中的k)加到这个1上面来提高你的得分。

二:加到哪个数字上去

由上述分析可知 ,你的得分会受到最小的数的影响。

如果你不加到最小的数上去,最大的得分并不会因此而改变。

因为如果你按最优化构造,最小的数肯定是已经和充分与其它数组合在一起了,加到其他地方上的数就变成了多余的。

所以我们的 k 一定是先用来抬高最小值的。

三:多出来的 k

样例一

2 4

8 4

添加之后从 8 4 -> 8 8

ans = n * (m - 1) + 1

n表示种类,就是题目中得那个n

m表示符合题意得子数组的数量,这里是8,因为能构造出8个[1,2]

算得答案为15,正确

其余样例可以根据上述思路进行验证,在此不一一赘述。

如果我们构造完了,构造出了这个

1 2 3 .... n

n位置上是出现次数最少的那一个数

那是不是我们还有办法在后面加?

如果还剩一个 4 的话

4 1 2 3 ... n 4

还剩 4 3的话

4 3 1 2 .. n 4 3

懂我意思了嘛?qwq

就是说,构造完剩下来的数,你还是可以放到后面去使得答案加1,多出来9个1,还是使得答案+1,那8个就是没用的了。

所以:

抛开和最小值构成的子数组,其他种类数都可以有办法再添加到数组后面去一个,

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int tt; cin >> tt;
	while (tt -- ) {
		int n, k; cin >> n >> k;
		vector<int> a(n + 1), pre(n + 1);
		for (int i = 1; i <= n; i ++ ) cin >> a[i];
		sort(a.begin() + 1, a.end());
		for (int i = 1; i <= n; i ++ ) {
			pre[i] = pre[i - 1] + (a[i] - a[i - 1]) * (i - 1);
		}
		int l = 0, r = n + 1;
		while (l + 1 != r) {
			int mid = l + r >> 1;
			if (pre[mid] <= k) l = mid;
			else r = mid;
		}
		k -= pre[l];
		int res = n * (a[l] + k / l - 1) + 1;
		k %= l;
		res += k + (n - l);
		cout << res << endl;
	}
	return 0;
}

代码解释:

是可以不用二分的,但是不知道为什么就用上了。

这个只跟数量有关,先sort。

这样二分出来的 l表示前 l 个数都可以在k的代价以内变成a[l] 

res = n * (a[l] + k / l - 1) + 1

n是种类

在k的代价以内都可以变成a[l],说明现在的最小值就是a[l]

子数组个数为 a[l] + k / l 个,这样最大化最小值了。

然后多余的可以放在后面,是 n - l个多余的种类,还有k%l个多余的。

D1题: Reverse Card (Easy Version)

题目大意:

a\epsilon [1, n], b\epsilon [1, m]

问有多少组(a, b)使得(a + b)b * gcd(a, b)的倍数

思路:

暴力。

hint:这个a是不是一定是b的倍数呀?

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int tt; cin >> tt;
	while (tt -- ) {
		//a+b是b⋅gcd(a,b)的倍数。
		int n, m; cin >> n >> m;
		int ans = 0;
		for (int b = 1; b <= m; b ++ ) {
			int a = b;
			while (a <= n) {
				if ((a + b) % (b * b) == 0) ans += 1;
				a += b;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值