ABC347 A-E题解


下面的内容不包括题目翻译,要想获取题目翻译,请参照 这篇教程 来获取题目翻译。

A

题目

我们对于每一个 A i A_i Ai 判断可不可以被 K K K 整除,即除以 K K K 是否为 0 0 0,如果是,输出 A i A_i Ai

AC Code:

#include <iostream>
using namespace std;
int n, k, a[110];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		if (a[i] % k == 0) cout << a[i] / k << ' ';
	}
	return 0;
}

B

题目

我们可以使用 STL(Standard Library) 里面的 map 来完成这个问题:对于每一个字串,判断是否出现过,没有出现过答案加一,并将这个字串标记为出现过。

一些语法:

s.substr(a, b); 截取 s 从 a 开始长度为 b 的字串。
map<string, bool> m; 创建一个 map 数组,其中每一个 string 都对应一个 bool 变量,使用:m[s]。

AC Code:

#include <iostream>
#include <cstring>
#include <map>
using namespace std;
string s;
map<string, bool> m;
int ans;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> s;
	for (int i = 0; i < (int)s.size(); i++) {
		for (int j = 1; i + j <= (int)s.size(); j++) {
			if (!m[s.substr(i, j)]) {
				ans++;
				m[s.substr(i, j)] = 1;
			}
		}
	}
	cout << ans;
	return 0;
}

C

题目

对于这道题,我们只用关心这些日子在一周中的相对位置就可以了。

我们发现:将这些日子的相对位置从小到大排序后,如果有两个日子之间的差大于 B B B,那么我们完全把这些工作日安排在这 B B B 天内。最后一个日子和第一个日子加上 A + B A+B A+B 天后的差大于 B B B 天,我们也可以得到一个合法解。

AC Code:

#include <algorithm>
#include <iostream>
using namespace std;
int n;
long long a, b;
long long d[200100];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> a >> b;
	for (int i = 1; i <= n; i++) cin >> d[i];
	for (int i = 1; i <= n; i++) {
		d[i] %= (a + b);
	}
	sort(d + 1, d + n + 1);
	d[n + 1] = d[1] + a + b;
	for (int i = 2; i <= n + 1; i++) {
		if (d[i] - d[i - 1] > b) {
			cout << "Yes";
			return 0;
		}
	}
	cout << "No";
	return 0;
}

D

题目

要解决这个问题,你首先要知道位运算是什么,如果不知道可以跳过这道题。

首先,我们判断无解的情况:

如果 a + b < popcount ( C ) a+b<\text{popcount}(C) a+b<popcount(C),那么无论怎么构造,都达不到 C C C

然后我们创造一种构造方法,使得这种构造方法可以在有解情况下一定可以构造出正解。

我们先来处理 C C C 1 1 1 的情况,拿第一个样例举例:

a = 3, b = 4, C = 00000111

我们先来看 C C C 中第 1 , 2 , 3 1,2,3 1,2,3 位,再看为 0 0 0 的地方。如果 C C C 的第 i i i 位为 0 0 0,那么这一位可以通过两种方式得到:两个 0 0 0 和两个 1 1 1。我们想让 X X X Y Y Y 构造完 1 1 1 的地方后剩余的 1 1 1 的数量相等以方便构造 0 0 0,所以我们每碰到一个 1 1 1 就让剩余 1 1 1 个数较大的那个数这一位为 1 1 1。如果剩余的 1 1 1 的数量不一样就说明无解。

再看异或的情况:如果 C C C 这一位为 0 0 0,且 a a a b b b 都不为 0 0 0,那么让 X X X Y Y Y 这一位都变为 1 1 1。如果构造完后还有没有构造的 1 1 1,说明无解。

这样就可以快速地构造出答案。

AC Code:

#include <iostream>
using namespace std;
long long a, b, c;
int cnt;
long long x, y;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> a >> b >> c;
	for (int i = 1; i <= 60; i++) cnt += ((c >> (i - 1)) & 1ll);
	if (a + b < cnt) {
		cout << "-1";
		return 0;
	}
	
	for (int i = 1; i <= 60; i++) {
		if (c & (1ll << (i - 1))) {
			if (a > b) {
				a--;
				x += (1ll << (i - 1));
			}
			else if (b) {
				b--;
				y += (1ll << (i - 1));
			}
		}
	}
	if (a != b) {
		cout << "-1";
		return 0;
	}
	for (int i = 1; i <= 60; i++) {
		if (a && b && !(c & (1ll << (i - 1)))) {
			a--;
			b--;
			x += (1ll << (i - 1));
			y += (1ll << (i - 1));
		}
	}
	if (a || b) {
		cout << "-1";
		return 0;
	}
	cout << x << ' ' << y;
	return 0;
}

E

题目

我们发现每一个元素出现的时间都是连续的,我们可以前缀和出集合大小,每一次元素的增减就把 A A A 相应的位置加上出现区间内集合的大小。用一个 last 变量维护元素出现位置。最后处理一下出现到查询末尾的元素即可。

用样例举例:

每一个时刻的集合:

{1}
{1,3}
{1}
{1,2}

每一个时刻的集合大小:

1
2
1
2

集合大小的前缀和:

1
3
4
6

每一个值出现位置:

1 -1 -1
1 -1 2
1 -1 -1
1 4 -1

于是 A A A 应该长这样:

A = [ ( s u m 4 − s u m 0 ) , ( s u m 4 − s u m 3 ) , ( s u m 2 − s u m 1 ) ] A=[(sum_4-sum_0),(sum_4-sum_3),(sum_2-sum_1)] A=[(sum4sum0),(sum4sum3),(sum2sum1)]

AC Code:

#include <iostream>
using namespace std;
int n, q;
int x[200100];
long long sum[200100];
int s[200100];
int cnt;
int last[200100];
long long a[200100];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q;
	for (int i = 1; i <= q; i++) cin >> x[i];
	for (int i = 1; i <= q; i++) {
		if (!s[x[i]]) {
			cnt++;
			sum[i] = sum[i - 1] + cnt;
			s[x[i]] = 1;
			last[x[i]] = i;
		}
		else {
			cnt--;
			sum[i] = sum[i - 1] + cnt;
			s[x[i]] = 0;
			a[x[i]] += sum[i - 1] - sum[last[x[i]] - 1];
		}
	}
	for (int i = 1; i <= n; i++) {
		if (s[i]) {
			a[i] += sum[q] - sum[last[i] - 1];
		}
	}
	for (int i = 1; i <= n; i++) cout << a[i] << ' ';
	return 0;
}
  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值