20230924 模拟赛总结
模拟赛链接
排名:\(\text{rank 1}\)
分数:\(100+100+100+100=400\)
集训期间第一次 AK!
T1:零用钱 / money
题目描述:
共 \(T\) 次询问,给你需要构造一个只有 \(s\) 和 \(-d\) 两个元素的数列,需要保证这个数列中所有长度为 \(k\) 的字串内的元素和小于零,且需要让这个序列的元素和是所有满足前面条件的序列集合中的序列元素和的最大值,如果能保证整个数列的元素和大于等于零输出和,否则输出 Deficit
。($1\leq T \leq100 $ , \(1\leq k \leq n \leq 10000\) , \(1\leq s,d \leq 1000\))
思路:
考虑每一个长度为 \(k\) 的字串,要么满足此要求:
(以上 \(i\) 为 \(s\) 的个数)
由于我们需要让总元素和最大,所以我们需要在满足以上条件的情况下让 \(i\) 尽量大,我们可以用枚举实现。这样我们就可以求出在最优解情况每个长度为 \(k\) 的字串有多少个 \(s\) 和 \(-d\),很容易发现如果要让总元素和最大那么每一个长度为 \(k\) 的字串的 \(s\) 和 \(-d\) 的数量都是一样的,这种方法可以通过用两个指针滑动窗口来实现。
时间复杂度:\(O(T\cdot n)\),空间复杂度:\(O(n)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 10005;
int T, n, k, s, d, x, y, p, q, a[kMaxN], ans;
int main() {
freopen("money.in", "r", stdin);
freopen("money.out", "w", stdout);
for (cin >> T; T; T--) {
cin >> n >> k >> s >> d;
for (int i = 0; i <= k; i++) {
if (i * s - (k - i) * d >= 0) {
x = i - 1, y = k - i + 1;
break;
}
}
p = n / k, q = n % k;
ans = s * x * p - d * y * p + min(q, x) * s - (q - min(q, x)) * d;
if (ans >= 0) {
cout << ans << '\n';
} else {
cout << "Deficit\n";
}
}
return 0;
}
T2:矩形求和 / matrix
题目描述:
给定一个 \(N \times M\) 的矩阵 \(A\) ,定义矩阵的分数是该矩阵中每个元素之和。给定正整数 \(R, C\),求问 \(A\) 中所有大小为 \(R\times C\) 的子矩阵的分数最大值。(\(1\leq N,M \leq 1000\), \(1\leq R \leq N\), $1\leq C \leq M $, \(1\leq A_{i,j}\leq 1000\))
思路:
这题一眼二位前缀和,不再赘述。
时间复杂度:\(O(n^2)\),空间复杂度:\(O(n^2)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1005;
int n, m, r, c, a[kMaxN][kMaxN], sum[kMaxN][kMaxN], ans;
int main() {
freopen("matrix.in", "r", stdin);
freopen("matrix.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> r >> c;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
}
}
for (int i = 1; i + r - 1 <= n; i++) {
for (int j = 1; j + c - 1 <= m; j++) {
ans = max(ans, sum[i + r - 1][j + c - 1] - sum[i - 1][j + c - 1] - sum[i + r - 1][j - 1] + sum[i - 1][j - 1]);
}
}
cout << ans;
return 0;
}
T3:射击 / dart
题目描述:
射击规则如下:最多射击 \(4\) 次,可以不射击。目标靶被分成 \(N\) 部分,每一部分的分值依次为 \(P_1, P_2, P_3, ... ,P_N\),射击总分为每次射击的分值之和。若分值总和超过 \(M\),得分变为 \(0\)。(\(1\leq N\leq 1000\),\(1\leq M,P_i\leq 2\times 10^8\))
思路:
经过一顿胡乱的做法,发现 dp 是不行滴,发现 \(N\) 的范围才 \(1000\),考虑时间复杂度 \(O(n^2)\)。
很容易想到先枚举前两个分值,知道前两个分值后该怎么办呢?从题目得知,若前两个选的物品的总和为 \(S\),后两个选的下标分别为 \(i,j\),我们有一个条件:\(S+P_i+P_j\le M\)。\(S\) 和 \(M\) 都是已知的,那就可以把已知的放在一边,未知的放在另一边,那么不等式变形为:\(P_i+P_j\le M-S\)。然后我们很容易发现发现这个不等式可以用来二分,先将 \(P_i+P_j\) 存在数组里然后排序,这样就可以二分了。
时间复杂度:\(O(n^2)\),空间复杂度:\(O(n^2\log^2 n)\)
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1005;
int n, m, a[kMaxN], b[kMaxN * kMaxN], tot, ans;
int main() {
freopen("dart.in", "r", stdin);
freopen("dart.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
b[++tot] = a[i] + a[j];
}
}
sort(b + 1, b + 1 + tot);
for (int i = 1, p; i <= tot; i++) {
if (b[i] > m) {
break;
}
p = upper_bound(b + 1, b + 1 + tot, m - b[i]) - b - 1;
b[i] + b[p] <= m && (ans = max(ans, b[i] + b[p]));
}
cout << ans;
return 0;
}
T4:序列合并 / sequence
题目描述:
洛谷原题:Luogu-P1631
有两个长度为 \(N\) 的单调不降序列 \(A,B\),在 \(A,B\) 中各取一个数相加可以得到 \(N^2\) 个和,求这 \(N^2\) 个和中最小的 \(N\) 个。(\(1 \le N \le 10^5\),\(1 \le a_i,b_i \le 10^9\))
思路:
这题是一道很板子的题目,首先将 \(a,b\) 数组排序。如果在当前 \(a_i+b_j\) 是最小值,那么下一个最小值只可能为,\(a_{i+1},b_j\) 或 \(a_i,b_{j+1}\),所以我们可以开一个优先队列 \(a_i+b_j\) 越小放在越前面,一开始把 \(a_1+b_1\) 加入,后面就将 \(a_2+b_1\) 和 \(a_1+b_2\) 加入优先队列,注意需要用 std::set
去重。
时间复杂度:\(O(n\log n)\),空间复杂度:\(O(n)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1e5 + 5;
int n, a[kMaxN], b[kMaxN];
struct P {
int i, j;
bool operator<(const P &y) const {
return a[i] + b[j] > a[y.i] + b[y.j];
}
} now;
priority_queue<P> q;
set<pair<int, int>> s;
int main() {
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
q.push({1, 1});
for (int i = 1; i <= n;) {
q.pop();
if (s.count({q.top().i, q.top().j})) {
continue;
}
now = q.top();
s.insert({now.i, now.j});
cout << a[now.i] + b[now.j] << ' ';
q.push({now.i + 1, now.j});
q.push({now.i, now.j + 1}), i++;
}
return 0;
}