The 2023 ICPC Asia Macau Regional Contest

PHarr發表於2024-10-02

A. (-1,1)-Sumplete

首先只取\(-1\),這樣的話選1和不選-1產生的貢獻就是都是 +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>;

const i32 inf = INT_MAX / 2;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vector a(n, vi(n, 1));

    string s;
    for (int i = 0; i < n; i++) {
        cin >> s;
        for (int j = 0; j < n; j++)
            if (s[j] == '-') a[i][j] = -1;
    }
    vi row(n), col(n);
    for (auto &i: row) cin >> i;
    for (auto &i: col) cin >> i;

    priority_queue<pii> heap;
    for (int j = 0; j < n; j++) {
        int need = col[j];
        for (int i = 0; i < n; i++)
            if (a[i][j] < 0) need++;
        if (need > 0) heap.emplace(need, j);
    }
    vector b(n, vi(n, -1));
    for (int i = 0; i < n; i++) {
        int t = row[i];
        for (int j = 0; j < n; j++)
            if (a[i][j] < 0) t++;
        if (t > heap.size()) {
            cout << "No\n";
            return 0;
        }
        vector<pii> cur;
        for (; t; t--)
            cur.push_back(heap.top()), heap.pop();
        for (auto [need, j]: cur) {
            need--, b[i][j] = 1;
            if (need > 0) heap.emplace(need, j);
        }
    }
    if (not heap.empty()) {
        cout << "No\n";
        return 0;
    }
    cout << "Yes\n";
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            cout << (a[i][j] == b[i][j]);
        cout << "\n";
    }

    return 0;
}

D. Graph of Maximum Degree 3

對於一個點為\(x\)的圖,最多隻有\(\frac{3x}{2}\)條邊,如果只用紅色邊聯通最少需要\(x-1\)條邊,如果只用藍色邊聯通最少也需要\(x-1\)條邊。因此符合條件的圖一定滿足

\[\frac{3x}{2} \ge 2x -2 \]

因此符合條件的圖點數不超過\(4\)。此時我們就可以分別建圖 ,然後列舉出聯通塊,然後找紅色聯通塊和藍色連通塊相同的個數。

#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>;

const i32 inf = INT_MAX / 2;

const int mod = 998244353;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<pii>> e(n + 1);

    for (int x, y, c; m; m--) {
        cin >> x >> y >> c;
        e[x].emplace_back(y, c);
        e[y].emplace_back(x, c);
    }

    set<vi> R, B;
    for (int x = 1; x <= n; x++) {
        for (auto [y, c1]: e[x]) {
            if (y > x) {
                if (c1) R.insert({x, y});
                else B.insert({x, y});
            }
            for (auto [z, c2]: e[x]) {
                if (y == z) continue;
                if (y < z) continue;

                if (c1 != c2) continue;
                vi t = {x, y, z};
                ranges::sort(t);
                if (c1) R.insert(t);
                else B.insert(t);

                for (auto [w, c3]: e[y]) {
                    if (w == x or w == y or w == z) continue;
                    if (c1 != c3) continue;
                    t = {w, x, y, z};
                    ranges::sort(t);
                    if (c1) R.insert(t);
                    else B.insert(t);
                }

                for (auto [w, c3]: e[z]) {
                    if (w == x or w == y or w == z) continue;
                    if (c1 != c3) continue;
                    t = {w, x, y, z};
                    ranges::sort(t);
                    if (c1) R.insert(t);
                    else B.insert(t);
                }
            }
        }
    }
    int res = n;
    for (auto it: R)
        if (B.count(it)) res++;
    cout << res % mod;
    return 0;
}

E. Inverse Topological Sort

我們觀察拓撲序,為什麼會出現逆序?一種最簡單的情況一定時逆序對之間出現了邊。

但是如果所有的逆序對之間都建邊,邊的數量可能很大,我們考慮對於每個點,先它前面最近的逆序對兩邊,這樣的話,邊數不會超過\(2n\)

當然了建出來圖也不一定就滿足條件,我們還要在求一下拓撲序。

#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>;

const i32 inf = INT_MAX / 2;

const int mod = 998244353;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vector<vi> e(n);
    vi a(n), b(n);
    stack<int> stk;
    for (int i = 0; i < n; i++) {
        cin >> a[i], a[i]--;
        while (not stk.empty() and stk.top() < a[i]) stk.pop();
        if (not stk.empty()) e[stk.top()].push_back(a[i]);
        stk.push(a[i]);
    }
    while (not stk.empty()) stk.pop();
    for (int i = 0; i < n; i++) {
        cin >> b[i], b[i]--;
        while (not stk.empty() and stk.top() > b[i]) stk.pop();
        if (not stk.empty()) e[stk.top()].push_back(b[i]);
        stk.push(b[i]);
    }

    vi c, d;
    vi inDegC(n);
    for (int x = 0; x < n; x++)
        for (auto y: e[x])
            inDegC[y]++;
    vi inDegD = inDegC;
    priority_queue<int, vi, greater<>> heapC;
    priority_queue<int> heapD;
    for (int i = 0; i < n; i++)
        if (inDegC[i] == 0) heapC.push(i), heapD.push(i);
    while (not heapC.empty()) {
        auto x = heapC.top();
        heapC.pop();
        c.push_back(x);
        for (auto y: e[x])
            if (--inDegC[y] == 0) heapC.push(y);
    }
    while (not heapD.empty()) {
        auto x = heapD.top();
        heapD.pop();
        d.push_back(x);
        for (auto y: e[x])
            if (--inDegD[y] == 0) heapD.push(y);
    }

    if (a != c or b != d) {
        cout << "NO";
        return 0;
    }
    cout << "YES\n";
    vector<pii> ret;
    for (int x = 0; x < n; x++)
        for (auto y: e[x])
            ret.emplace_back(x, y);
    cout << ret.size() << "\n";
    for (auto [x, y]: ret)
        cout << x + 1 << " " << y + 1 << "\n";
    return 0;
}

H. Random Tree Parking

\(dep_i\)表示\(i\)的深度,\(size_i\)表示\(i\)的子樹的大小。這樣的話\(i\)及其子樹大小中,可以選擇的點的個數就是\([size_i,size_i+dep_i]\)

我們可以用樹形dp,計算出\(f_{i,j}\)表示\(i\)為根的子樹中選擇了\(j+size_i\)的方案數。其中\(j\le dep_i\)

注意到樹是完全隨機的,因此可以保證深度是在\(\log n\)附近,因此複雜度是\(O(n\log n)\)

#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>;

const i32 inf = INT_MAX / 2;

const int mod = 998244353;

struct mint {
    i64 x;

    mint(int x = 0) : x(x) {}

    int val() const {
        return (x % mod + mod) % mod;
    }

    mint &operator=(int o) { return x = o, *this; }

    mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; }

    mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; }

    mint &operator*=(mint o) { return x = (i64) x * o.x % mod, *this; }

    mint &operator^=(int b) {
        mint w = *this, ret(1);
        for (; b; b >>= 1, w *= w) if (b & 1) ret *= w;
        return x = ret.x, *this;
    }

    mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); }

    friend mint operator+(mint a, mint b) { return a += b; }

    friend mint operator-(mint a, mint b) { return a -= b; }

    friend mint operator*(mint a, mint b) { return a *= b; }

    friend mint operator/(mint a, mint b) { return a /= b; }

    friend mint operator^(mint a, int b) { return a ^= b; }
};

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;

    vector<mint> fact(n + 1), invFact(n + 1);
    fact[0] = 1;
    for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
    invFact[n] = fact[n] ^ (mod - 2);
    for (int i = n; i; i--) invFact[i - 1] = invFact[i] * i;

    auto C = [fact, invFact, n](int x, int y) -> mint {
        return fact[x] * invFact[y] * invFact[x - y];
    };

    vector<vi> e(n + 1);
    vi dep(n + 1);
    dep[1] = 1;
    for (int i = 2, x; i <= n; i++) {
        cin >> x;
        dep[i] = dep[x] + 1;
        e[x].push_back(i);
    }

    auto conv = [C](vector<mint> &a, vector<mint> &b, int n, int x, int y) -> void {
        vector<mint> g(n + 1, 0);
        for (int i = 0; i <= n; i++)
            for (int j = 0; i + j <= n; j++)
                g[i + j] += a[i] * b[j] * C(x + y + i + j, x + i);
        a = move(g);
    };

    vector<vector<mint>> f(n + 1);
    vi size(n + 1);
    for (int x = n; x; x--) {
        f[x] = vector<mint>(dep[x] + 1, 1);
        for (auto y: e[x]) {
            conv(f[x], f[y], dep[x], size[x], size[y]);
            size[x] += size[y];
        }
        size[x]++;
        for (int i = 0; i < dep[x]; i++)
            f[x][i] = f[x][i + 1];
    }
    cout << f[1][0].val() << "\n";
    return 0;
}

I. Refresher into Midas

貪心的考慮,對於\(a\)一定是能用就用最好。多用\(b\)不會使得答案變差。

對於\(b\)有兩種情況,一種情況是能用就用,還有一種時等待\(a\)迴轉,在使用\(a\)後立即使用\(b\)再使用\(a\)

第一種情況的週期是\(b\),收益是\(\left \lfloor \frac{b}{a} \right \rfloor + 1\)

第二種情況的週期是\(\left \lceil \frac{b}{a} \right \rceil a\),收益是\(\left \lceil \frac{b}{a} \right \rceil +1\)

對於兩種週期我們可以貪心的列舉一個,再計算出另一個週期的個數,對於最後剩下的時間就一直使用\(a\)就好了。

#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>;

const i32 inf = INT_MAX / 2;

void solve() {
    int a, b, m;
    cin >> a >> b >> m;
    int t1 = b, v1 = b / a + 1;
    int t2 = ((b + a - 1) / a) * a, v2 = (b + a - 1) / a + 1;

    int res = 2;
    for (int c1 = 0; c1 <= m / t1; c1++) {
        int c2 = (m - c1 * t1) / t2;
        int c3 = (m - c1 * t1 - c2 * t2) / a;
        res = max(res, c1 * v1 + c2 * v2 + c3 + 2);
    }
    cout << 160 * res << "\n";
    return;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}
 

J. Teleportation

題目的操作,其實可以轉換成兩種操作。第一種是移動\(a_i\)步。第二種是移動\(1\)步。但是第二種操作前,必須有第一種操作。

這樣的話直接做 bfs 即可

#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>;

const i32 inf = INT_MAX / 2;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    vi a(n);
    for (auto &i: a) cin >> i;

    vector dis(n, vi(2, inf));
    vector vis(n, vi(2));

    queue<pii> q;


    dis[0][0] = 0;
    q.emplace(0, 0);
    while (not q.empty()) {
        auto [x, t] = q.front();
        q.pop();
        if (vis[x][t]) continue;
        vis[x][t] = 1;
        int y = (x + a[x]) % n;
        if (dis[y][1] > dis[x][t] + 1) {
            dis[y][1] = dis[x][t] + 1;
            q.emplace(y, 1);
        }

        y = (x + 1) % n;
        if (t == 1 and dis[y][1] > dis[x][1] + 1) {
            dis[y][1] = dis[x][1] + 1;
            q.emplace(y, 1);
        }
    }
    cout << ranges::min(dis[k]);
    return 0;
}
 

相關文章