2023 CCPC 秦皇岛
A. Make SYSU Great Again I
因为\(k \ge 2n\),所以可以顺序按照以阶梯形状摆放,这样可以保证每行每列两个,且\(\gcd\)都是 1,剩下的数字随便放就好了。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k;
cin >> n >> k;
int t = 1;
for (int i = 1; i <= n and t <= k; i++) {
cout << i << " " << i << "\n", t++;
if (i == n)
cout << i << " " << 1 << "\n", t++;
else
cout << i << " " << i + 1 << "\n", t++;
}
for (int i = 1; i <= n and t <= k; i++) {
for (int j = 1 + ( i == n); j < i and t <= k; j++) {
cout << i << " " << j << "\n", t++;
}
for (int j = i + 2; j <= n and t <= k; j++) {
cout << i << " " << j << "\n", t++;
}
}
return 0;
}
D. Yet Another Coffee
考虑一种贪心的情况,把所有的打折卡按照结束时间排序,然后我们逐个打折卡考虑,我们把打折卡给最便宜的咖啡使用一定最优,因为打折卡是使得价格为负数。操作完后,贪心的选择最便宜的即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
void solve() {
int n, m;
cin >> n >> m;
vi a(n);
for (auto &i: a) cin >> i;
vector<pii> b(m);
for (auto &[r, w]: b) cin >> r >> w;
ranges::sort(b);
multiset<int> pre;
int t = 0;
for (int x; auto [r, w]: b) {
while (t < n and t < r) pre.insert(a[t++]);
x = *pre.begin(), pre.erase(pre.begin()), pre.insert(x - w);
}
while (t < n) pre.insert(a[t++]);
for (int i = 1, res = 0; i <= n; i++) {
res += *pre.begin(), pre.erase(pre.begin());
cout << res << " ";
}
cout << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
F. Mystery of Prime
找规律发现对于两个奇偶相同的数\(x,y\),存在一个数\(k\)可以使得\(x+k,y+k\)均为质数。然后相邻的两个数奇偶必须不同,除了一种特殊情况,就是两个\(1\)相邻。
因此我们可以设计状态\(f[i][0/1/2/3]\)表示前\(i\)个数,且第\(i\)个数的状态为\(0\)不改变、\(1\)修改为\(1\)、\(2\)修改为奇数、\(3\)修改为偶数。
然后转移就可以分为两类,一种相邻两个数,我们根据刚才说的奇偶不同和两个\(1\)的情况进行转移。还有一种是相邻三个数,两侧的数字就相同的情况。
#include<bits/stdc++.h>
using namespace std;
using vi = vector<int>;
const int inf = INT_MAX / 2;
bool is_prime(int x) {
if (x < 2) return false;
for (int i = 2; i * i <= x; i++)
if (x % i == 0) return false;
return true;
}
int main() {
int n;
cin >> n;
vi a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
if (n == 2) {
if (is_prime(a[1] + a[2])) cout << 0;
else cout << 1;
return 0;
}
vector f(n + 1, vi(4, inf));// 0 不变, 1 变1, 2 变奇, 3 变偶
f[1][0] = 0;
f[1][1] = (a[1] != 1);
f[1][2] = f[1][3] = 1;
for (int i = 2; i <= n; i++) {
// 0
if (is_prime(a[i] + a[i - 1])) f[i][0] = min(f[i][0], f[i - 1][0]);
if (is_prime(a[i] + 1)) f[i][0] = min(f[i][0], f[i - 1][1]);
if (a[i] % 2 == 1) f[i][0] = min(f[i][0], f[i - 1][3]);
else f[i][0] = min(f[i][0], f[i - 1][2]);
// 1
if (is_prime(1 + a[i - 1])) f[i][1] = min(f[i][1], f[i - 1][0] + (a[i] != 1));
f[i][1] = min(f[i][1], f[i - 1][1] + (a[i] != 1));
f[i][1] = min(f[i][1], f[i - 1][3] + (a[i] != 1));
// 2
if (a[i - 1] % 2 == 0) f[i][2] = min(f[i][2], f[i - 1][0] + 1);
f[i][2] = min(f[i][2], f[i - 1][3] + 1);
// 3
if (a[i - 1] % 2 == 1) f[i][3] = min(f[i][3], f[i - 1][0] + 1);
f[i][3] = min(f[i][3], f[i - 1][1] + 1);
f[i][3] = min(f[i][3], f[i - 1][2] + 1);
// 改中间
f[i][1] = min(f[i][1], f[i - 2][1] + 1 + (a[i] != 1)); // 1 x 1
f[i][1] = min(f[i][1], f[i - 2][2] + 1 + (a[i] != 1)); // 奇 x 1
f[i][2] = min(f[i][2], f[i - 2][2] + 2); // 奇 x 奇
f[i][2] = min(f[i][2], f[i - 2][1] + 2); // 1 x 奇
f[i][3] = min(f[i][3], f[i - 2][3] + 2); // 偶 x 偶
if (a[i] % 2 == 1) { // ai 是 奇
f[i][0] = min(f[i][0], f[i - 2][1] + 1); // 1 x ai
f[i][0] = min(f[i][0], f[i - 2][2] + 1); // 奇 x ai
if (a[i - 2] % 2 == 1) f[i][0] = min(f[i][0], f[i - 2][0] + 1); // ai-2 x ai
} else { // ai 是 偶
f[i][0] = min(f[i][0], f[i - 2][3] + 1);// 偶 x ai
if (a[i - 2] % 2 == 0) f[i][0] = min(f[i][0], f[i - 2][0] + 1); // ai-2 x ai
}
}
cout << ranges::min(f[n]);
return 0;
}
G. Path
手推一下发现,其实答案就是\(\sum|a_i - a_{i-1}| + \sum |b_i - b_{i-1}|\)
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
vi a(n), b(m);
for (auto &i: a) cin >> i;
for (auto &i: b) cin >> i;
int res = 0;
for (int i = 1; i < n; i++)
res += abs(a[i] - a[i - 1]);
for (int i = 1; i < m; i++)
res += abs(b[i] - b[i - 1]);
cout << res;
return 0;
}
J. Keyi LIkes Reading
其实只有13个物品,然后我们可以设计状态\(f[i][j]\)表示前\(i\)天选了\(j\)是否成立。其中\(j\)是二进制表示那些被选了。
因此我们可以用\(O(13\times (2^{13})^2)\)的代价转移出来。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
vi cnt(13);
int n, w;
cin >> n >> w;
for (int i = 1, x; i <= n; i++)
cin >> x, cnt[x - 1]++;
ranges::sort(cnt, greater<>());
n = 0;
while (n < 13 and cnt[n] != 0) n++;
cnt.resize(n);
vi can;
int N = 1 << n;
for (int i = 1, sum; i < N; i++) {
sum = 0;
for (int j = 0; j < n; j++)
if ((i >> j) & 1) sum += cnt[j];
if (sum <= w) can.push_back(i);
}
vi f(N);
f[0] = 1;
for (int i = 1; i <= n; i++) {
auto g = f;
for (int t = 1; t < N; t++) {
for (auto p: can) {
if ((t | p) != t) continue;
g[t] |= f[t ^ p];
}
}
f = move(g);
if (f[N - 1]) {
cout << i << "\n";
return 0;
}
}
return 0;
}
然后赛时因为写错了一点,导致越界进而导致 TLE。我按头写了一个优化,首先我们倒序枚举,这样可以优化空间,然后对于状态\(j\),我强制要求当前学习的单词中包含了\(j\)的\(msg\),这样的话可以把复杂度减小\(\frac{1}{13}\)。\(msg\)是最高有效位。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
vi cnt(13);
int n, w;
cin >> n >> w;
for (int i = 1, x; i <= n; i++)
cin >> x, cnt[x - 1]++;
ranges::sort(cnt, greater<>());
n = 0;
while (n <= 13 and cnt[n] != 0) n++;
cnt.resize(n);
auto msg = [&](int x) {
for (int i = n - 1; i >= 0; i--) {
if ((x >> i) & 1) return i;
}
};
vector<vi> can(n);
int N = 1 << n;
for (int i = 1, sum; i < N; i++) {
sum = 0;
for (int j = 0; j < n; j++)
if ((i >> j) & 1) sum += cnt[j];
if (sum <= w) can[msg(i)].push_back(i);
}
vi f(N);
f[0] = 1;
for (int i = 1; i <= n; i++) {
for (int t = N - 1; t >= 1; t--) {
if (f[t]) continue;
for (auto p: can[msg(t)]) {
if ((t | p) != t) continue;
if (f[t ^ p] == 0)continue;
f[t] = 1;
break;
}
}
if (f[N - 1]) {
cout << i << "\n";
return 0;
}
}
return 0;
}