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;
}
posted @ 2025-08-29 09:31  zzy0618  阅读(7)  评论(0)    收藏  举报