2023年 8月8日普及组南外集训题解
A 国家集训队
题解
注意数据已经是有序的,我还搞了个排序,我是智障 所以只需要将第5个人到第16个人的成绩都预设成300,再把前4个人的成绩都预设成0,再看有没有人能超过第4个人就行了
ac代码
#include <iostream>
using namespace std;
const int N = 20;
int a[N], ans = 4;
int main() {
for (int i = 1; i <= 16; i++) cin >> a[i];
for (int i = 5; i <= 16; i++) {
if (a[i] + 300 > a[4])
ans++;
else
break;
}
cout << ans;
return 0;
}
B 撑伞
题解
想要所有人都不被淋,那么最长的距离就是 \(n+2×D\) ,每个人如果有伞的话,能覆盖到的地方就是 \((1+2×D)\),相除就行了
ac代码
#include <iostream>
using namespace std;
int n, k;
int main() {
cin >> n >> k;
cout << (n + 2 * k) / (2 * k + 1);
return 0;
}
C 走遍城市
题解
非常简单的数学题,也就是找出来一个数 \(x\) ,使得它和初始位置能凑出来 \(p_i\) 就行了,具体看代码
ac代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int p[N];
int n, x, ans;
int main() {
cin >> n >> x;
for (int i = 1; i <= n; i++) {
int k;
cin >> k;
ans = __gcd(ans, abs(k - x));
}
cout << ans;
return 0;
}
D 魔法筷子
题解
我们可以只考虑第三个情况,一二情况直接贪心
我们可以假设我们的 A B C 筷子初始为0,然后累加
注意最后减30,因为等于0的时候,实际上不是合并筷子
ac 代码
#include <iostream>
using namespace std;
const int N = 10;
int l[N];
int n, ans = 0x3f3f3f3f, A, B, C;
void dfs(int u, int x, int y, int z, int magic) {
if (x && y && z)
ans = min(ans, abs(x - A) + abs(y - B) + abs(z - C) + magic - 30);
if (u > n)
return;
dfs(u + 1, x + l[u], y, z, magic + 10);
dfs(u + 1, x, y + l[u], z, magic + 10);
dfs(u + 1, x, y, z + l[u], magic + 10);
dfs(u + 1, x, y, z, magic);
}
int main() {
cin >> n >> A >> B >> C;
for (int i = 0; i < n; i++) cin >> l[i];
dfs(0, 0, 0, 0, 0);
cout << ans;
return 0;
}
E 马农
题解
这道题可以用二维前缀和来优化
注意这题有两种情况,题目的图片有就不多赘述了
我使用了一个cnt数组来记录收益的情况并加了一个偏移量来防止负数的情况
首先两重循环枚举交点,注意是枚举到 \(n-1\) ,不然就没位置了,然后再分别枚举两个矩形的位置,把第一个矩形算出来的收益用cnt数组记录,然后另外一个矩形的收益如果跟第一个矩形一样,答案就加上
不能用memset初始化,我们可以把所有用到的位置记录下来,然后将这些位置归0就行了,不然会2000万会爆
ac代码
#include <iostream>
using namespace std;
int x;
const int N = 105, convertnum = 1000000; //防止负数
short cnt[20000005];
int nums[N]; //去重
int sum[N][N];
int top, ans, n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> x;
sum[i][j] = x + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
}
}
//两种情况,一种两个都横着,一种一个竖着,一个横着
// i枚举到n-1,不然没位置了 j同理
for (int i = 1; i < n; i++) {
for (int j = 1; j < n; j++) {
for (int k = 1; k <= i; k++) {
for (int l = 1; l <= j; l++) {
int tmp = sum[i][j] - sum[i][l - 1] - sum[k - 1][j] + sum[k - 1][l - 1];
if (!cnt[tmp + convertnum])
nums[++top] = tmp + convertnum;
cnt[tmp + convertnum]++;
}
}
for (int k = n; k > i; k--) {
for (int l = n; l > j; l--) {
int tmp = sum[k][l] - sum[i][l] - sum[k][j] + sum[i][j];
ans += cnt[tmp + convertnum];
}
}
//后面还要用
for (int k = 1; k <= top; k++) cnt[nums[k]] = 0;
top = 0;
}
}
for (int i = 1; i < n; i++) {
for (int j = n; j > 1; j--) {
for (int k = 1; k <= i; k++) {
for (int l = n; l >= j; l--) {
int tmp = sum[i][l] - sum[k - 1][l] - sum[i][j - 1] + sum[k - 1][j - 1];
if (!cnt[tmp + convertnum])
nums[++top] = tmp + convertnum;
cnt[tmp + convertnum]++;
}
}
for (int k = n; k > i; k--) {
for (int l = 1; l < j; l++) {
int tmp = sum[k][j - 1] - sum[k][l - 1] - sum[i][j - 1] + sum[i][l - 1];
ans += cnt[tmp + convertnum];
}
}
for (int k = 1; k <= top; k++) cnt[nums[k]] = 0;
top = 0;
}
}
cout << ans;
return 0;
}