Skip to content

2026夏个人训练赛第二十一场

A. 数列排序

  • 排序

无脑把最小的往前换即可。

cpp
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;
long long a[N], b[N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        b[i] = a[i];
    }
    sort(b + 1, b + n + 1);
    for (int i = 1; i <= n; ++i) {
        a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
    }
    for (int i = 1; i <= n; ++i) {
        b[a[i]] = i;
    }
    int res = 0;
    for (int i = 1; i <= n; ++i) {
        if (a[i] != i) {
            res++;
            swap(a[i], a[b[i]]);
            swap(b[a[i]], b[a[b[i]]]);
        }
    }
    cout << res << '\n';
    return 0;
}

B. 分蛋糕

  • 暴力

直接暴力枚举。

cpp
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

LL solve(LL n, LL m) {
    LL res = n % 3 == 0 ? 0 : m;
    for (int i = 1; i < n; ++i) {
        LL s1 = i * m, s2 = (m / 2) * (n - i), s3 = (m + 1) / 2 * (n - i);
        res = min(res, max(abs(s1 - s2), max(abs(s1 - s3), abs(s2 - s3))));
    }
    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    LL n, m;
    cin >> n >> m;
    cout << min(solve(n, m), solve(m, n)) << '\n';
    return 0;
}

C. 冰岛

  • 广度优先搜索
  • 图论

分别遍历每行每列,对于所有极大连续 0,所有点分别向左端点和右端点连边,然后直接 bfs 最短路。

cpp
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

typedef long long LL;
const int N = 1010, M = N * N;
int a[N][N];
vector<int> adj[M];
bool vis[M];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> a[i][j];
        }
    }
    auto get = [n](int x, int y) -> int {
        return (x - 1) * n + y;
    };
    for (int i = 1; i <= n; ++i) {
        int l = 0, r = 0;
        for (int j = 1; j <= n; ++j) {
            if (a[i][j] == 0) {
                if (!l) l = j;
                r = j;
            }
            else if (l) {
                adj[get(i, l)].emplace_back(get(i, r));
                adj[get(i, r)].emplace_back(get(i, l));
                for (int k = l + 1; k < r; ++k) {
                    adj[get(i, k)].emplace_back(get(i, l));
                    adj[get(i, k)].emplace_back(get(i, r));
                }
                l = 0;
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        int l = 0, r = 0;
        for (int j = 1; j <= n; ++j) {
            if (a[j][i] == 0) {
                if (!l) l = j;
                r = j;
            }
            else if (l) {
                adj[get(l, i)].emplace_back(get(r, i));
                adj[get(r, i)].emplace_back(get(l, i));
                for (int k = l + 1; k < r; ++k) {
                    adj[get(k, i)].emplace_back(get(l, i));
                    adj[get(k, i)].emplace_back(get(r, i));
                }
                l = 0;
            }
        }
    }

    int xs, ys, xt, yt;
    cin >> xs >> ys >> xt >> yt;
    int s = get(xs, ys), t = get(xt, yt);
    queue<pair<int, int>> q;
    q.emplace(0, s);
    vis[s] = true;
    while (!q.empty()) {
        auto [d, x] = q.front();
        q.pop();
        if (x == t) {
            cout << d << '\n';
            return 0;
        }
        for (int y : adj[x]) {
            if (!vis[y]) {
                vis[y] = true;
                q.emplace(d + 1, y);
            }
        }
    }
    cout << "impossible\n";
    return 0;
}

D. 项链

  • 动态规划
  • 排序

按照周长排序,然后 n2 做 DP,因为大的一定不可能在小的里面套着,保证了无后效性。

cpp
#include <iostream>
#include <tuple>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long LL;
const int N = 1010;
tuple<LL, LL, LL> a[N];
int f[N];

bool check(tuple<LL, LL, LL> a, tuple<LL, LL, LL> b) {
    if (a == tuple<LL, LL, LL>(0, 0, 0)) return true;
    else if (a == b) return false;
    else {
        auto &[l1, x1, y1] = a;
        auto &[l2, x2, y2] = b;
        return double((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) < double((l2 - l1) * (l2 - l1)) / (4.0 * acos(-1) * acos(-1));
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        auto &[x, y, z] = a[i];
        cin >> y >> z >> x;
    }
    sort(a + 1, a + n + 1);
    int res = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < i; ++j) {
            if (check(a[j], a[i])) f[i] = max(f[i], f[j] + 1);
        }
        res = max(res, f[i]);
    }
    cout << res << '\n';
    return 0;
}

E. 旅游

  • 贪心
  • 排序

预留一个尽可能短的从车站走到 B,剩下的从大到小贪心尝试一定最优。

cpp
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;
const int N = 500010;
LL a[N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    LL m, d, n;
    cin >> m >> d >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];

    sort(a + 1, a + n + 1, greater<LL>());

    int ridx = -1;
    LL lim = 1e18 + 10;
    if (d != m) {
        for (int i = n; i; --i) {
            if (a[i] >= m - d) {
                ridx = i;
                lim = (d - (a[i] - (m - d)) / 2);
                break;
            }
        }
        if (!ridx) {
            cout << "0\n";
            return 0;
        }
    }

    LL cur = 0;
    int res = 0;
    for (int i = 1; i <= n; ++i) {
        if (i == ridx) continue;
        if (cur >= min(lim, d)) break;
        else if (d - cur >= a[i]) {
            cout << "0\n";
            return 0;
        }
        else cur += a[i] - (d - cur), res++;
    }
    if (cur >= m) cout << res << '\n';
    else if (cur >= lim && ridx != -1) cout << res + 1 << '\n';
    else cout << "0\n";
    return 0;
}

F. Milk Pails【Normal】

  • 动态规划

直接暴力 DP 即可。

cpp
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;
bool f[N][N][N];

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int x, y, k, m;
    cin >> x >> y >> k >> m;
    f[0][0][0] = true;
    for (int i = 0; i < k; ++i) {
        for (int j = 0; j <= x; ++j) {
            for (int l = 0; l <= y; ++l) {
                if (!f[i][j][l]) continue;
                // fill
                f[i + 1][x][l] = f[i + 1][j][y] = true;
                // empty
                f[i + 1][0][l] = f[i + 1][j][0] = true;
                // pour j -> l
                int c = min(y - l, j);
                f[i + 1][j - c][l + c] = true;
                // pour l -> j
                c = min(x - j, l);
                f[i + 1][j + c][l - c] = true;
            }
        }
    }
    int res = m;
    for (int i = 0; i <= x; ++i) {
        for (int j = 0; j <= y; ++j) {
            if (f[k][i][j]) res = min(res, abs(m - i - j));
        }
    }
    cout << res << '\n';
    return 0;
}

G. Lasers and Mirrors

  • 0-1 BFS
  • 图论

直接离散化行和列,每行每列按照另一个坐标的值,相邻的点连边,并标记来自行还是列,然后用 deque 跑 0/1 最短路。

cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <deque>

using namespace std;

const int N = 100010;
unordered_map<int, vector<pair<int, int>>> row, col;
vector<pair<int, int>> adj[N];
bool vis[N][2];

void insert(int x, int y, int idx) {
    row[x].emplace_back(y, idx);
    col[y].emplace_back(x, idx);
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int n, xl, yl, xb, yb;
    cin >> n >> xl >> yl >> xb >> yb;
    insert(xl, yl, 0);
    insert(xb, yb, n + 1);
    for (int i = 1; i <= n; ++i) {
        int x, y;
        cin >> x >> y;
        insert(x, y, i);
    }

    for (auto &[_, v] : row) {
        sort(v.begin(), v.end());
        for (int i = 0; i < v.size() - 1; ++i) {
            adj[v[i].second].emplace_back(v[i + 1].second, 0);
            adj[v[i + 1].second].emplace_back(v[i].second, 0);
        }
    }
    for (auto &[_, v] : col) {
        sort(v.begin(), v.end());
        for (int i = 0; i < v.size() - 1; ++i) {
            adj[v[i].second].emplace_back(v[i + 1].second, 1);
            adj[v[i + 1].second].emplace_back(v[i].second, 1);
        }
    }

    // dis idx dir
    deque<tuple<int, int, int>> q;
    q.emplace_back(0, 0, 0);
    q.emplace_back(0, 0, 1);
    int res = 0x3f3f3f3f;
    while (!q.empty()) {
        auto [d, x, t] = q.front();
        q.pop_front();
        if (vis[x][t]) continue;
        vis[x][t] = true;

        if (x == n + 1) res = min(res, d);

        for (auto [y, tt] : adj[x]) {
            int w = tt ^ t;
            if (w) q.emplace_back(d + w, y, tt);
            else q.emplace_front(d + w, y, tt);
        }
    }
    cout << (res == 0x3f3f3f3f ? -1 : res ) << '\n';
    return 0;
}

其他没做的题

  • Pipeline Scheduling
  • Rikka with Lowbit
  • Rikka with Burrow-Wheeler Transform
  • Rikka with Rotate
  • Rikka with Prefix Sum
  • Rikka with Equation