AtCoder Beginner Contest 221【A - G】


比賽鏈接:https://atcoder.jp/contests/abc221/tasks

A - Seismic magnitude scales

題意

給出兩個正整數 \(a, b\) ,計算 \(32^{a - b}\)

  • \(3 \le b \le a \le 9\)

題解

\(32^{a - b} = 2 ^ {5(a - b)}\)

代碼

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int a, b;
    cin >> a >> b;
    cout << (1 << (5 * (a - b))) << "\n";
    return 0;
}

B - typo

題意

給出兩個字符串 \(s, t\) ,判斷能否交換 \(s\) 中至多一對相鄰字符,使得 \(s\)\(t\) 相同。

  • \(2 \le |s| = |t| \le 100\)

題解

枚舉交換的位置,同時判斷前后的 \(s, t\) 是否相同即可。

代碼

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s, t;
    cin >> s >> t;
    int n = s.size();
    auto query = [&](int l, int r) {
        for (int i = l; i <= r; i++) {
            if (s[i] != t[i]) {
                return false;
            }
        }
        return true;
    };
    bool ok = (s == t);
    for (int i = 0; i + 1 < n; i++) {
        if (s[i] == t[i + 1] and s[i + 1] == t[i] and query(0, i - 1) and query(i + 2, n - 1)) {
            ok = true;
        }
    }
    cout << (ok ? "Yes" : "No") << "\n";
    return 0;
}

C - Select Mul

題意

給出一個正整數 \(n\) ,將其分為兩個非空子序列,子序列可以重新排列。

計算在所有可能的情況中,兩個子序列之積的最大值。

  • \(1 \le n \le 10^9\)

題解

枚舉所有分類情況即可,時間復雜度約為 \(2^{len(n)}\)

代碼

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    cin >> s;
    int n = s.size();
    int ans = 0;
    for (int i = 0; i < (1 << n); i++) {
        string a, b;
        for (int j = 0; j < n; j++) {
            (i & (1 << j) ? a : b) += s[j];
        }
        if (a.empty() or b.empty()) {
            continue;
        }
        sort(a.begin(), a.end(), greater<>());
        sort(b.begin(), b.end(), greater<>());
        ans = max(ans, stoi(a) * stoi(b));
    }
    cout << ans << "\n";
    return 0;
}

D - Online games

題意

給出 \(n\) 個區間起點 \(a_i\) 及區間長度 \(b_i\) ,計算有多少個數恰好被 \(1, \dots, n\) 個區間覆蓋。

  • \(1 \le n \le 2 \times 10^5\)
  • \(1 \le a_i, b_i \le 10^9\)

題解一

將區間端點離散化后差分進行區間加和,然后遍歷一次所有點即可。

代碼

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<pair<int, int>> seg(n);
    vector<int> disc;
    for (int i = 0; i < n; i++) {
        int a, b;
        cin >> a >> b;
        seg[i] = {a, a + b - 1};
        for (int j = -1; j <= 1; j++) {
            disc.push_back(a + j);
            disc.push_back(a + b - 1 + j);
        }
    }
    sort(disc.begin(), disc.end());
    disc.resize(unique(disc.begin(), disc.end()) - disc.begin());
    const int N = disc.size();
    vector<int> cnt(N);
    map<int, int> mp;
    for (auto [l, r] : seg) {
        int nl = lower_bound(disc.begin(), disc.end(), l) - disc.begin();
        int nr = lower_bound(disc.begin(), disc.end(), r) - disc.begin();
        mp[nl] = l, mp[nr] = r;
        ++cnt[nl], --cnt[nr + 1];
    }
    for (int i = 1; i < N; i++) {
        cnt[i] += cnt[i - 1];
        mp[i] = max(mp[i], mp[i - 1] + 1);
    }
    vector<int> ans(n + 1);
    for (int i = 1; i < N; i++) {
        ans[cnt[i]] += mp[i + 1] - mp[i];
    }
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << " \n"[i == n];
    }
    return 0;
}

題解二

比較巧妙的一種離散化構造方法。

代碼

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<pair<int, int>> seg;
    for (int i = 0; i < n; i++) {
        int a, b;
        cin >> a >> b;
        seg.emplace_back(a, 1);
        seg.emplace_back(a + b, -1);
    }
    sort(seg.begin(), seg.end());
    vector<int> ans(n + 1);
    int cnt = 0;
    for (int i = 0; i + 1 < 2 * n; i++) {
        cnt += seg[i].second;
        ans[cnt] += seg[i + 1].first - seg[i].first;
    }
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << " \n"[i == n];
    }
    return 0;
}

E - LEQ

題意

給出一個長為 \(n\) 的整數序列 \(a\) ,計算有多少首部小於等於尾部,長度至少為 \(2\) 的子序列,對 \(998244353\) 取模。

  • \(2 \le n \le 3 \times 10^5\)
  • \(1 \le a_i \le 10^9\)

題解

對於 \(a_j\) 來說,以它為尾部滿足條件的子序列個數為 \(\sum \limits _{i \lt j, a_i \le a_j} 2^{j - i - 1}\) ,即 \(2 ^ j \times \sum \limits _{i \lt j, a_i \le a_j} \frac{1}{2^{i + 1}}\)

類似權值樹狀數組的思想,以對之后的數的貢獻 \(\frac{1}{2^{i + 1}}\)\(a_i\) 進行更新即可。

代碼

#include <bits/stdc++.h>
using namespace std;

constexpr int MOD = 998244353;

int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }

struct Z {
    int x;
    Z(int x = 0) : x(norm(x)) {}
    int val() const { return x; }
    Z operator-() const { return Z(norm(MOD - x)); }
    Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
    Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
    Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
    Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
    Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
    friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
    friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
    friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
    friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
};

struct Fenwick_tree {
    vector<Z> bit;

    Fenwick_tree(int n) : bit(n + 1) {}

    void update(int pos, Z val) {
        for (int i = pos; i < (int)bit.size(); i += i & (-i)) {
            bit[i] += val;
        }
    }

    Z query(int pos) {
        Z res = 0;
        for (int i = pos; i >= 1; i -= i & (-i)) {
            res += bit[i];
        }
        return res;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<int> b(a);
    sort(b.begin(), b.end());
    b.resize(unique(b.begin(), b.end()) - b.begin());
    for (auto& i : a) {
        i = lower_bound(b.begin(), b.end(), i) - b.begin() + 1;
    }
    Z ans = 0;
    Fenwick_tree F(n);
    for (int i = 0; i < n; i++) {
        ans += F.query(a[i]) * binpow(Z(2), i);
        F.update(a[i], binpow(Z(2), i + 1).inv());
    }
    cout << ans.val() << "\n";
    return 0;
}

F - Diameter set

題意

給出一棵大小為 \(n\) 的樹,計算有多少種染色方案(至少染 \(2\) 個結點),使得任意染色結點之間的距離均為樹的直徑。

  • \(2 \le n \le 2 \times 10^5\)

題解

將樹從任意一條直徑中間切開,根據直徑長度的奇偶性分為兩種情況

  • 奇數:移去中邊
  • 偶數:移去與中點相連的邊

此時每棵子樹內的結點與切開處的根節點距離至多為 \(\lfloor \frac{diam - 1}{2} \rfloor\) ,即一棵子樹內最多只可能染一個結點。

設每棵子樹內與切開處的根節點距離為 \(\lfloor \frac{diam - 1}{2} \rfloor\) 的結點個數為 \(x_i\) , 答案即 \(\prod \limits (x_i + 1) - \sum \limits x_i - 1\)

含義為從所有染色情況中減去只染一個或不染的情況。

代碼

#include <bits/stdc++.h>
using namespace std;

constexpr int MOD = 998244353;

int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }

struct Z {
    int x;
    Z(int x = 0) : x(norm(x)) {}
    int val() const { return x; }
    Z operator-() const { return Z(norm(MOD - x)); }
    Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
    Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
    Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
    Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
    Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
    friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
    friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
    friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
    friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<set<int>> G(n);
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        G[u].insert(v);
        G[v].insert(u);
    }
    vector<int> dis(n), fa(n);
    function<void(int, int)> dfs1 = [&](int u, int p) {
        fa[u] = (p == -1 ? -1 : p);
        dis[u] = (p == -1 ? 0 : dis[p] + 1);
        for (auto v : G[u]) {
            if (v != p) {
                dfs1(v, u);
            }
        }
    };
    dfs1(0, -1);
    int ua = max_element(dis.begin(), dis.end()) - dis.begin();
    dfs1(ua, -1);
    int ub = max_element(dis.begin(), dis.end()) - dis.begin();
    vector<int> path;
    for (int u = ub; u != -1; u = fa[u]) {
        path.push_back(u);
    }
    reverse(path.begin(), path.end());
    vector<int> root;
    int diam = dis[ub];
    if (diam & 1) {
        int u = path[diam / 2], v = path[diam / 2 + 1];
        G[u].erase(v);
        G[v].erase(u);
        root.push_back(u);
        root.push_back(v);
    } else {
        int u = path[diam / 2];
        root.assign(G[u].begin(), G[u].end());
        for (auto v : G[u]) {
            G[v].erase(u);
        }
        G[u].clear();
    }
    vector<int> v;
    int cnt = 0;
    function<void(int, int, int)> dfs2 = [&](int u, int p, int d) {
        if (d == (diam - 1) / 2) {
            ++cnt;
        }
        for (auto v : G[u]) {
            if (v != p) {
                dfs2(v, u, d + 1);
            }
        }
    };
    for (auto u : root) {
        cnt = 0;
        dfs2(u, -1, 0);
        v.push_back(cnt);
    }
    Z ans = 1;
    for (auto x : v) {
        ans *= (x + 1);
    }
    for (auto x : v) {
        ans -= x;
    }
    ans -= 1;
    cout << ans.val() << "\n";
    return 0;
}

G - Jumping sequence

題意

在一個平面內可以上下左右移動,初始時位於 \((0, 0)\) 處,給出每步移動的距離 \(d_i\) ,判斷能否恰好 \(n\) 步后到達 \((a, b)\) 處,如果可以,輸出一種方案。

  • \(1 \le n \le 2000\)
  • \(|a|,|b| \le 3.6 \times 10^6\)
  • \(1 \le d_i \le 1800\)
  • 輸入均為整數

題解

比較巧妙的一種構造方案。

將原 \((x, y)\) 坐標系逆時針旋轉 45° 映射到 \((x + y, x - y)\) 坐標系中,此時上下左右移動的四種情況分別映射到 \((\pm d_i, \pm d_i)\)

然后問題轉化為尋找一種對 \(s_i, s'_i\)\(+1, -1\) 賦值方案使得:

\[\sum \limits _{i = 1} ^{n} s_i d_i = x + y, \sum \limits _{i = 1} ^{n} s'_i d_i = x - y \]

兩邊加上 \(s = \sum \limits _{i = 1} ^{n} d_i\) ,此時問題轉化為了尋找一種對 \(t_i, t'_i\)\(0, 1\) 賦值方案使得:

\[\sum \limits _{i = 1} ^{n} t_i d_i = \frac{s + x + y}{2}, \sum \limits _{i = 1} ^{n} t'_i d_i = \frac{s + x - y}{2} \]

使用 bitset 進行狀壓枚舉所有賦值方案即可。

因為轉換后坐標系的特殊性,在回溯后輸出方案時每一步需同時考慮 \(t_i, t'_i\) 的賦值情況。

代碼

#include <bits/stdc++.h>
using namespace std;
constexpr int M = 2000 * 1800 + 10;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, a, b;
    cin >> n >> a >> b;
    vector<int> d(n);
    int s = 0;
    for (int i = 0; i < n; i++) {
        cin >> d[i];
        s += d[i];
    }
    bool ok = true;
    for (auto i : {a + b, a - b}) {
        if ((s + i) % 2 != 0 or (s + i) / 2 < 0 or (s + i) / 2 > s) {
            ok = false;
        }
    }
    if (not ok) {
        cout << "No" << "\n";
        return 0;
    }
    int x = (s + a + b) / 2, y = (s + a - b) / 2;
    vector<bitset<M>> bit(n + 1);
    bit[0][0] = 1;
    for (int i = 0; i < n; i++) {
        bit[i + 1] = bit[i] | (bit[i] << d[i]);
    }
    if (not bit[n][x] or not bit[n][y]) {
        cout << "No" << "\n";
        return 0;
    }
    vector<bool> addx(n), addy(n);
    for (int i = n - 1; i >= 0; i--) {
        if (not bit[i][x]) {
            x -= d[i];
            addx[i] = true;
        }
        if (not bit[i][y]) {
            y -= d[i];
            addy[i] = true;
        }
    }
    cout << "Yes" << "\n";
    for (int i = 0; i < n; i++) {
        cout << (addx[i] ? (addy[i] ? 'R' : 'U') : (addy[i] ? 'D' : 'L'));
    }
    cout << "\n";
    return 0;
}

參考

https://atcoder.jp/contests/abc221/editorial/2733

https://atcoder.jp/contests/abc221/editorial/2732

https://atcoder.jp/contests/abc221/editorial/2731

https://atcoder.jp/contests/abc221/editorial/2729


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM