ABC300 A-E 题解

14 篇文章 0 订阅

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

A

题目

看看哪一个 i i i 满足 C = A + B C_ = A+B C=A+B 就可以了,如果满足,输出这个 i i i 就可以走人了。

AC Code:

#include <iostream>
using namespace std;
int n, a, b;
int c[101000];

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 >> c[i];
		if (c[i] == a + b) {
			cout << i;
			return 0;
		}
	}
	return 0;
}

B

题目

我们发现, H H H W W W 都很小,所以我们枚举垂直移动的次数和水平移动的次数。

我们用一个 t e m p temp temp 数组来记录当前移动后的地图,设我们执行了 x x x 次水平移动和 y y y 次垂直移动,那么我们的 t e m p temp temp 数组应该长这样:
∑ i = 1 H ∑ j = 1 W t e m p i j = A ( ( i + x − 1 ) mod H ) ( ( j + y − 1 ) mod W ) \sum_{i=1}^H\sum_{j=1}^W temp_{ij}=A_{((i+x-1)\text{mod}H)((j+y-1)\text{mod}W)} i=1Hj=1Wtempij=A((i+x1)modH)((j+y1)modW)
这里下标从 1 1 1 开始。

解释一下原理:

每一次执行水平移动, A ( i + 1 ) j A_{(i+1)j} A(i+1)j 会变成原来的 A i j A_{ij} Aij,而 A 1 j A_{1j} A1j 会变成原来的 A N j A_{Nj} ANj,相当于第一个下标统一加上一再模上 H H H,每一次垂直移动, A i j + 1 A_{ij+1} Aij+1 会变成 A i j A_{ij} Aij,而 A i 1 A_{i1} Ai1 会变成 A i W A_{iW} AiW,相当于第二个下标统一加上一再模上 W W W。如此这样分别做 x x x 次和 y y y 次就是我们上面的式子。

最后判断一下是不是每一个 B i j B_{ij} Bij 等于 t e m p i j temp_{ij} tempij 就可以了。

AC Code:

#include <iostream>
using namespace std;
int h, w;
char a[1010][1010], b[1010][1010];
char tmp[1010][1010];
bool check(int s, int t) {
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j <= w; j++) {
			tmp[i][j] = a[(i + s - 2) % h + 1][(j + t - 2) % w + 1];
			if (tmp[i][j] != b[i][j]) return 0;
		}
	}
	return 1;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> h >> w;
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j <= w; j++) cin >> a[i][j];
	}
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j <= w; j++) cin >> b[i][j];
	}
	for (int s = 1; s <= h; s++) {
		for (int t = 1; t <= w; t++) {
			if (check(s, t)) {
				cout << "Yes";
				return 0;
			}
		}
	}
	cout << "No";
	return 0;
}

C

题目

这里我们枚举一每一个点为中心的叉即可。我们判断出这个叉的大小,对应大小的计数器加上一。最后输出计数器里的内容即可。

为了算出每一个点为中心的叉的大小,我们先假设这个叉大小为 0 0 0,如果当前大小的叉存在,那么扩大叉的范围,再试。由于对于每一个大小的叉,之前大小的叉已经试过了,每一次尝试只有 O ( 1 ) O(1) O(1) 的时间复杂度。而叉的大小不超过 min ⁡ ( H , W ) \min(H,W) min(H,W),所以计算以每一个点为中心的叉的大小的时间复杂度是 min ⁡ ( H , W ) \min(H,W) min(H,W)

3 ≤ H , W ≤ 100 3 \le H,W \le 100 3H,W100,枚举点 H W HW HW 个,一个 min ⁡ ( H , W ) \min(H,W) min(H,W),极限情况下是 1 0 6 10^6 106,合法的时间复杂度。

AC Code:

#include <iostream>
using namespace std;
int h, w;
char c[200][200];
int cnt[200];
int check(int x, int y) {
	int d = 0;
	while (x + d <= h && x - d >= 1 && y + d <= w && y - d >= 1 && c[x + d][y + d] == '#' && c[x - d][y + d] == '#' && c[x + d][y - d] == '#' && c[x - d][y - d] == '#') d++;
	return d - 1;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> h >> w;
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j <= w; j++) cin >> c[i][j];
	}
	for (int i = 1; i <= h; i++) {
		for (int j = 1; j <= w; j++) {
			cnt[check(i, j)]++;
		}
	}
	for (int i = 1; i <= min(h, w); i++) cout << cnt[i] << ' ';
	return 0;
}

D

题目

此题预处理出质数,一个一个枚举 a , b , c a,b,c a,b,c,由于 N ≤ 1 0 1 2 N \le 10^12 N1012,这样枚举大概只有 N 3 5 N^{\frac{3}{5}} N53 的时间复杂度,预处理出质数,处理到 ( N ) \sqrt(N) ( N) 的质数是没问题的。预处理质数用最朴素的试除法,时间复杂度不大, O ( N N ) O(\sqrt{N}\sqrt{\sqrt{N}}) O(N N ),大约 1 0 9 10^9 109,可以在 3 3 3 秒钟的时间内解决。

AC Code:

#include <iostream>
#define int long long
using namespace std;
int n;
int cnt, cntt;
int primes[1000100];

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 2; i <= 1000000; i++) {
		bool flag = 0;
		for (int j = 2; j * j <= i; j++) {
			if (i % j == 0) {
				flag = 1;
				break;
			}
		}
		if (!flag) primes[++cntt] = i;
	}
	for (int i = 1; primes[i] * primes[i] * primes[i] * primes[i] * primes[i] <= n; i++) {
		for (int j = i + 1; primes[i] * primes[i] * primes[j] * primes[j] * primes[j] <= n; j++) {
			for (int k = j + 1; primes[i] * primes[i] * primes[j] * primes[k] * primes[k] <= n; k++) {
				cnt++;
			}
		}
	}
	cout << cnt;
	return 0;
}

E

多用 AtCoder Library。

题目

核心:记忆化搜索。

我们直接上记忆化动态规划,如果这个条件试过了,就直接退出。这个过程中只有是 2 , 3 , 5 2,3,5 2,3,5 的倍数的数会被枚举。再 N N N 取到极限范围用容斥原理算一下有多少个:

log ⁡ 2 N log ⁡ 3 N log ⁡ 5 N \log_2^N\log_3^N\log_5^N log2Nlog3Nlog5N

这个式子大概在 N N N 1 0 1 8 10^18 1018 的情况下,不会超过 1 0 6 10^6 106

套上一个 mod998244353 秒掉。

对于每一个 d p i dp_i dpi,记录符合条件的和所有数字的情况的比值。
如果 i ≥ N i\ge N iN
如果 i = N i=N i=N,那么 d p i = 1 dp_i=1 dpi=1,否则 d p i = 0 dp_i=0 dpi=0
否则:
d p i dp_i dpi d p 2 i d p 3 i d p 4 i d p 5 i d p 6 i 5 \frac{dp_{2i}dp_{3i}dp_{4i}dp_{5i}dp_{6i}}{5} 5dp2idp3idp4idp5idp6i,由于 1 1 1 乘上任何数还是它自己,所以没必要算。正确答案是 d p 1 dp_1 dp1

如果不会用 AtCoder Library 的,也可以自己写一个分数加减法套上扩展欧几里得。

AC Code:

#include <iostream>
#include <atcoder/all>
#include <map>
#define int long long
using namespace std;
using namespace atcoder;
using mint = modint998244353;
int n;
map<int, mint> dp;
mint sol(int now) {
	if (now >= n) return now == n ? 1 : 0;
	if (dp.count(now)) return dp[now];
	mint ret = 0;
	for (int i = 2; i <= 6; i++) ret += sol(i * now);
	return dp[now] = ret / 5;
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	cout << sol(1).val();
	return 0;
}
  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值