2025.8.29模拟赛
前情提要:全年龄段只用 2h 就可以获得 330pts。
T1
先排序,枚举每个数成为中位数的情况,然后必然有 \(2\) 个小于,\(2\) 个大于,这个在其他数组二分就行。对于相等只允许后面与现在相等即可。
赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, P = 998244353;
int n, ans;
int ask(vector<int> &a, int x) { return upper_bound(a.begin(), a.end(), x) - a.begin(); }
int ksa(vector<int> &a, int x) { return a.end() - lower_bound(a.begin(), a.end(), x); }
vector<int> a[6];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0, x; i < 5; ++i) {
for (int j = 0; j < n; ++j)
cin >> x, a[i].emplace_back(x);
sort(a[i].begin(), a[i].end());
}
for (int j = 0; j < 5; ++j)
for (int i = 0; i < n; ++i) {
for (int s = 0; s < (1 << 5); ++s) {
if ((s >> j) & 1)
continue;
if (__builtin_popcount(s) != 2)
continue;
int tmp = 1;
for (int l = 0; l < 5; ++l) {
if (l == j)
continue;
if ((s >> l) & 1)
tmp = 1ll * tmp * ask(a[l], a[j][i] - (l < j)) % P;
else
tmp = 1ll * tmp * ksa(a[l], a[j][i] + (l > j)) % P;
}
ans = (1ll * tmp * a[j][i] % P + ans) % P;
}
}
cout << ans << '\n';
return 0;
}
T2
给定一个 \(n\) 个点 \(m\) 条边的有向图。不保证不存在重边自环。
记 \(f(x,y,k)\) 表示从 \(x\) 到 \(y\) 经过恰好 \(k\) 条边的不同路径数量称一个有向图是有趣的,当且仅当存在 \(x,y\),不存在任何正整数 \(M\),使得\(k≥M\) 时 \(f(x,y,k)\) 为一常数,也即 \(lim_{k\to\infty} f(x,y,k)\) 不存在。判断给定的有向图是不是有趣的。
先将重边扔掉,如果存在环那一定是坐实了,如果只有自环就要判断是否由一个自环可以到达另一个自环。然后就行了。
赛时代码
#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N = 5e5 + 5;
int n, m, d[N], f[N];
vector<int> e[N];
queue<int> q;
int zyq;
signed main() {
// freopen("ex_data2.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1, u, v; i <= m; ++i)
cin >> u >> v, (u == v ? ++f[u] : (e[u].eb(v), 1));
for (int i = 1; i <= n; ++i) {
sort(e[i].begin(), e[i].end());
e[i].erase(unique(e[i].begin(), e[i].end()), e[i].end());
for (int v : e[i])
++d[v];
}
for (int i = 1; i <= n; ++i)
if (!d[i])
q.push(i);
while (q.size()) {
int u = q.front();
++zyq, q.pop();
if (f[u] > 1)
return cout << "Yes\n", 0;
for (int v : e[u]) {
f[v] += f[u];
if (!(--d[v]))
q.push(v);
}
}
if (zyq < n)
cout << "Yes\n";
else
cout << "No\n";
return 0;
}
T3
有 \(n\) 堆石子,第 \(i\) 堆石子有 \(a_i\) 个,\(A\) 和 \(B\) 玩游戏,\(A\) 先手,每次操作可以进行以下操作:
选定一个还有石子的石子堆 \(i\),记剩下的石子为 \(a_i′\)。
选定一个 \(1\le x\le a_i'\),将该堆中的 \(x\) 个石子捏碎。
选定一个 \(0\le y\le a_i'−x\),将该堆中的 \(y\) 个石子以任意方式分配到剩余的非空石子堆中。
第一个不能操作者输,问 \(A\) 是否有必胜,多测。
首先这是 Nim 的加强,直观来看后手想赢会更难。
直接将 Nim 的条件拿过来,然后发现 1 2 3 就可以将其 ban 掉,而像 2 2 5 5, 1 1 4 4 这种依然准确。
然后发现 \(n\) 为偶数排完序后数字成对出现的 \(A\) 必输,因为 \(B\) 只要做对称操作即可。然后就把这个交上去就对了。
考虑证明,若 \(n\) 为奇数,\(A\) 开局用最大的那组把剩下所有填成成对出现,否则 \(A\) 开局选最大和最小的那一对组合,将用最大的那一堆填平剩下的是剩下的成对出现,而最终最大的那堆与最小的那堆相等。
赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int T, n, a[N];
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
if (n & 1)
return cout << "Yes\n", void();
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i += 2)
if (a[i] != a[i + 1])
return cout << "Yes\n", void();
cout << "No\n";
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
for (cin >> T; T; --T)
solve();
return 0;
}
T4
对于一个数 \(x=\overline{a_1,a_2,\dots,a_n}\),可以选择一个 \(a_i\) 将 \(x\) 变为 \(x+a_i\) 或 \(x-a_i\)。\(t\) 次询问 \(x,y\),求 \(x\) 变为 \(y\) 的最小操作次数,无解输出 \(-1\),强制在线。
\(1\le x,y,t\le 10^5\)
爆搜有 30pts。
其实出题人给只给 \(10^5\) 说明这题肯定有较大常数或复杂度,至少肯定不是单 \(\log\)。
然后数字单次变化量只有 \(9\),也就是对于 \(x,y\) 之间任意长度为 \(9\) 的区间都会至少走过一次。
考虑分治,对于每个分治区间,求出所有 \(s\in [mid-4,mid+4]\) 到所有 \(t\in [l-30,r+30]\) 的正图与反图的最短距离,之所以有一个 \(30\) 是考虑过程不会超过 \(l,r\) 过多。。。
然后每次查询的时候看一下 \(x,y\) 是否经过 \(mid\),若经过则枚举 \(mid-4\sim mid+4\) 求最小值,否则继续递归。
复杂度瓶颈是预处理,为 \(O(kn\log n)\),\(k\) 为进制数。
赛后代码
#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mid (l + r >> 1)
#define eb emplace_back
using namespace std;
const int N = 100050, F = 1e9;
int T, x, y, ans;
vector<int> f[N], g[N], a[N], b[N];
void init() {
for (int i = 0; i <= 100030; ++i) {
for (int j = i; j; j /= 10)
f[i].eb(i - j % 10), f[i].eb(i + j % 10);
sort(f[i].begin(), f[i].end());
f[i].erase(unique(f[i].begin(), f[i].end()), f[i].end());
for (int j : f[i])
g[j].eb(i);
}
}
queue<int> q;
void bfs(int s, vector<int> *e, vector<int> &d) {
d.resize(y - x + 1, F), d[s - x] = 0;
for (q.push(s); q.size();) {
int u = q.front();
q.pop();
for (int v : e[u])
if (x <= v && v <= y && d[v - x] == F)
d[v - x] = d[u - x] + 1, q.push(v);
}
}
void solve(int l, int r) {
if (l >= r)
return;
solve(l, mid - 5), solve(mid + 5, r);
x = max(0, l - 30), y = r + 30;
for (int i = max(mid - 4, l); i <= min(mid + 4, r); ++i)
bfs(i, f, a[i]), bfs(i, g, b[i]);
}
void ask(int l, int r) {
if (l > r)
return;
if (max(mid - 4, min(x, y)) <= min(mid + 4, max(x, y))) {
int z = max(0, l - 30);
for (int i = max(mid - 4, l); i <= min(mid + 4, r); ++i)
ans = min(ans, b[i][x - z] + a[i][y - z]);
return;
}
y <= mid ? ask(l, mid - 5) : ask(mid + 5, r);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
init(), solve(0, 100000);
for (cin >> T; T; --T) {
cin >> x >> y, x ^= ans + 1, y ^= ans + 1, ans = F, ask(0, 100000);
cout << (ans == F ? (ans = -1) : ans) << '\n';
}
return 0;
}

浙公网安备 33010602011771号