CF1895 ECR div.2 现场
Result

被 xlwang 狠狠地吊打。
场切:
A
题意:有一条数轴,你站在原点上,宝箱、钥匙分别在 \(x, y\) 所在位置上。你可以在钥匙所在处捡起钥匙并携带着,也可以携带箱子走不超过 \(k\) 的距离。问能使宝箱和钥匙在同一位置上要走的最短距离。\(1\leq x,y\leq 100, 0\leq k\leq 100\)。
Solution
直接分讨。当 \(x \geq y\) 时,直接带着钥匙到宝箱处,\(ans = x\);\(x < y - k\) 时把箱子搬到 \(x + k\) 上,再到 \(y\) 拿钥匙,折返回 \(x + k\) 开箱,\(ans = 2y - x - k\);\(x - k \geq y\) 时,直接把箱子搬到钥匙处,\(ans = y\)。
\(\\\)
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
int T;
int x, y, k;
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while (T--) {
cin >> x >> y >> k;
if (x < y - k) {
cout << y + (y - (x + k)) << '\n';
} else if (x >= y) {
cout << x << '\n';
} else {
cout << y << '\n';
}
}
return 0;
}
B
题意:给定一个长度为 \(2n\) 的序列,你需要把他划分为两个长度为 \(n\) 的序列,第 \(i\) 个数分别表示路径上第 \(i\) 个点的 \(x, y\) 坐标。两点间距离为曼哈顿距离,路径长度为路径序列上相邻两点距离之和。\(2\leq n\leq 100\)。
Solution
你发现实际上 \(x\) 与 \(y\) 坐标是相互独立的,可以独立考虑。显然划分出的两个序列必定是升/降序的,否则变成升/降序可以使路径长度缩减重复走的部分。而这两个序列必定是将序列 \(a\) 排序后的 \([1, n]\) 和 \([n + 1, 2n]\) 这两个区间,否则必定会多出右序列最左端到左序列最右端这部分。
我数组忘开两倍,怒吃一罚时,时间优势变作无。
\(\\\)
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 200 + 1;
int T;
int n;
int a[kN];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= 2 * n; i++) {
cin >> a[i];
}
sort(a + 1, a + 2 * n + 1);
cout << a[n] - a[1] + a[2 * n] - a[n + 1] << '\n';
for (int i = 1; i <= n; i++) {
cout << a[i] << ' ' << a[i + n] << '\n';
}
}
return 0;
}
C
题意:给定 \(n\) 个数字串,可以选择两个串将其拼接,问总共有多少选法能使拼接后的串长度 \(l\) 为偶数,且区间 \([1, \frac{l}{2}]\) 与 \([\frac{l}{2} + 1, l]\) 内数字之和相等。\(1\leq n\leq 2\times 10^5\),数字串长度不超过 \(5\)。
Solution
我一开始想着能拼的长度就只有 \({i, i}, {1, 3}, {3, 5}, {1, 5}, {2, 4}\),直接暴力分讨。。。但是巨大麻烦。考虑统计各大情况。设 \(f_{0/1, l, c, s}\) 表示这个数字串拼在前/后面,以后拼成的数字串长度为 \(l\),当前数字串长度为 \(c\),这个数字串对以后拼成的数字串的前/后一半的和的贡献为 \(s\)。然后暴力统计暴力贡献就行了。
\(\\\)
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e5 + 1, kL = 10 + 1, kV = 50;
int n;
vector<int> a, c;
int f[2][kL][kL][2 * kV];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n;
for (int i = 1, w, l; i <= n; i++) {
cin >> w;
a.clear();
for (int x = w; x > 0; x /= 10) {
a.emplace_back(x % 10);
}
reverse(a.begin(), a.end());
for (int j = (a.size() + 1) / 2 * 2; j <= 10; j += 2) {
int s = 0;
for (int k = 0; k < a.size(); k++) {
if (k < j / 2) {
s += a[k];
} else {
s -= a[k];
}
}
f[0][j][a.size()][s + kV]++;
s = 0, c = a;
reverse(c.begin(), c.end());
for (int k = 0; k < c.size(); k++) {
if (k < j / 2) {
s += c[k];
} else {
s -= c[k];
}
}
f[1][j][a.size()][s + kV]++;
}
}
LL ans = 0;
for (int i = 2; i <= 10; i += 2) {
for (int j = 1; j <= max(i - 1, 5); j++) {
for (int k = 0; k < 2 * kV; k++) {
ans += 1ll * f[0][i][j][k] * f[1][i][i - j][k];
}
}
}
cout << ans << '\n';
return 0;
}
D
题意:给定一个长度为 \(n - 1\) 的序列 \(a\),需要你构造一个长度为 \(n\) 的序列 \(b\) 使其满足 \(b_i \oplus b_{i + 1} = a_i\),且 \(0\leq b_i \leq n-1\) 且两两不同。
Solution
首先 xor 差分是可以直接前缀 xor 回去得到的,我们可以直接通过序列 \(a\) 得到序列 \(b\) 区间 \([2, n]\) 中 xor 上 \(b_1\) 的所有数。我们可以直接枚举 \(b_1\) 判断得到的序列 \(b\) 是否合法。因为保证有解,所有只需要保证 \(\max\limits_{1\leq i\leq n} b_i \leq n - 1\) 即可。异或后最大值,01trie 板子。
不过天天狗叫 01trie 老哥好像没做出来/cf
\(\\\)
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e5 + 1, kM = 30 * kN;
int n;
int a[kN];
int t[kM][2], tot;
void I(int x) {
int p = 0;
for (int i = 20, c; i >= 0; i--) {
c = x >> i & 1;
if (!t[p][c]) {
t[p][c] = ++tot;
}
p = t[p][c];
}
}
int Q(int x) {
int p = 0, ret = 0;
for (int i = 20, c; i >= 0; i--) {
c = x >> i & 1;
if (t[p][!c]) {
p = t[p][!c];
ret = 2 * ret + 1;
} else {
p = t[p][c];
ret = 2 * ret;
}
}
return ret;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n;
for (int i = 2; i <= n; i++) {
cin >> a[i];
}
int ans = 0;
for (int i = 1; i <= n; i++) {
a[i] ^= a[i - 1], I(a[i]);
}
for (int i = 0; i < n; i++) {
if (Q(i) == n - 1) {
for (int j = 1; j <= n; j++) {
cout << (a[j] ^ i) << ' ';
}
cout << '\n';
return 0;
}
}
return 0;
}
补题
E
题意:小 M 和小 B 分别有 \(n\)、\(m\) 张卡牌,有相应的攻击值和防御值。一开始小 M 选出自己的一张牌打出,下一个人需要给出 攻击值比对方打出的牌的防御值严格更大的卡牌,谁无法按此规则出牌就判作那方战败,持续 \(10^{1000}\) 步就平局。问小 M 分别有多少张牌放在最开始给出可以使得最终自己 获胜/平局/战败。\(1\leq n, m\leq 3\times 10^5\)。
Solution
你发现不管是谁,最优策略总是选出攻击值比对方防御值严格更大,且防御值尽量大的卡牌。于是你对于一张牌,对方将会出什么样的牌是可确定的。考虑并查集上将当前牌并向对方会出的牌,一张牌当前连通块的根节点就是你出这张牌最终会以什么牌结束。可以直接判断出这张牌是谁的牌来预测胜负。时间复杂度 \(O((n+m)\log n)\),由于无法按秩合并,无法将复杂度优化到反阿克曼级别。
\(\\\)
Code
// stODDDDDDDDDDDDDDDDDDDDDDDDDDD hzt CCCCCCCCCCCCCCCCCCCCCCCCCOrz
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 3e5 + 2;
#define y first
#define x second
int T;
int n, m;
PII a[kN], b[kN];
int ans[2 * kN], v[2 * kN], fa[2 * kN];
int R(int x) {
if (x == fa[x]) {
return x;
}
fa[x] = R(fa[x]);
return fa[x];
}
void M(int u, int v) {
u = R(u), v = R(v);
if (u == v) {
return;
}
fa[u] = v;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].x;
}
for (int i = 1; i <= n; i++) {
cin >> a[i].y;
}
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> b[i].x;
}
for (int i = 1; i <= m; i++) {
cin >> b[i].y;
}
sort(a + 1, a + n + 1);
sort(b + 1, b + m + 1);
fill_n(v + 1, n + m, 0);
fill_n(ans + 1, n + m, 0);
iota(fa, fa + n + m + 1, 0);
for (int i = 1, j = m; i <= n; i++) {
for (; j && b[j].x <= a[i].y; j--) {
}
if (j != 0) {
v[i] = j + n;
M(i, j + n);
} else {
break;
}
}
for (int i = 1, j = n; i <= m; i++) {
for (; j && a[j].x <= b[i].y; j--) {
}
if (j != 0) {
v[i + n] = j;
M(i + n, j);
} else {
break;
}
}
for (int i = 1; i <= n + m; i++) {
if (v[i]) {
continue;
}
if (i <= n) {
ans[R(i)] = -1;
} else {
ans[R(i)] = 1;
}
}
int ans1 = 0, ans2 = 0, ans3 = 0;
for (int i = 1; i <= n; i++) {
int f = R(i);
if (ans[f] == -1) {
ans1++;
} else if (ans[f] == 0) {
ans2++;
} else {
ans3++;
}
}
cout << ans1 << ' ' << ans2 << ' ' << ans3 << '\n';
}
return 0;
}
F
题意:求存在多少个长度为 \(n\) 的序列 \(a\) 使相邻两数差不超过 \(k\),且数列中存在至少一个数在区间 \([x, x + k - 1]\) 内。\(1\leq n, k\leq 10^9, 0\leq x\leq 40\)。
Solution
注意到假设我们确定了差分数组和最小值,就可以得到整个序列的具体值。这是显然的,因为差分数组可以前缀和回去得到所有元素和 \(a_1\) 的差,又知道最小值就可以推出全部。同时,两个序列如果差分序列或最小值不同,两者显然不同。
于是我们可以试求 \(\min\limits_{1\leq i\leq n}a_i \leq x+k-1\) 且 \(|a_i-a_{i+1}|\) 的不同序列个数。发现存在 \((2k+1)^{n-1}\) 种不同的差分序列和 \(x+k\) 种最小值满足条件。只需要再除去 \(\max\limits_{1\leq i\leq n}a_i<x\) 的方案数即可。
发现这个东西很可 dp。由于序列中数的值域仅为 \([0, x]\),因此直接设 \(f_{i, j}\) 表示当前在第 \(i\) 位,且 \(a_i = j\) 的方案数量,暴力转移实在不可接受。但是由于相邻两数差不超过 \(k\) 是当前唯一的限制,直接矩阵快速幂优化即可。时间复杂度 \(O(x^3\log n)\)。
\(\\\)
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kX = 40 + 1, kP = 1e9 + 7;
int T;
int n, x, k;
struct Mat {
int a[kX][kX];
Mat() { fill(a[0], a[x] + x + 1, 0); }
Mat operator*(const Mat &o) const {
Mat ret;
for (int i = 0; i < x; i++) {
for (int k = 0; k < x; k++) {
for (int j = 0; j < x; j++) {
ret.a[i][j] = (ret.a[i][j] + 1ll * a[i][k] * o.a[k][j]) % kP;
}
}
}
return ret;
}
} a, ret;
int P(int a, int b) {
int ret = 1;
for (; b > 0; b /= 2) {
if (b & 1) {
ret = 1ll * ret * a % kP;
}
a = 1ll * a * a % kP;
}
return ret;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while (T--) {
cin >> n >> x >> k;
int ans = 1ll * P(2 * k + 1, n - 1) * (x + k) % kP;
for (int i = 0; i < x; i++) {
for (int j = 0; j < x; j++) {
a.a[i][j] = (abs(i - j) <= k);
}
}
ret = Mat();
int b = n - 1;
for (int i = 0; i < x; i++) {
ret.a[0][i] = 1;
}
for (; b > 0; b /= 2) {
if (b & 1) {
ret = ret * a;
}
a = a * a;
}
for (int i = 0; i < x; i++) {
ans = (ans - ret.a[0][i] + kP) % kP;
}
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号