AtCoder Beginner Contest 350 A - G 題解

Martian148發表於2024-04-23

AtCoder Beginner Contest 350

A - Past ABCs

Solution

把最後三個字元轉成數字判斷即可

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    string s; cin >> s;
    s = s.substr(3,3);
    int x = 0;
    x = (s[0] -'0') * 100 + (s[1] - '0') * 10 + (s[2] - '0');
    if (x >= 350)
        return cout << "No" << endl, 0;
    if (x == 316)
        return cout << "No" << endl, 0;
    if (x <= 0)
        return cout << "No" << endl, 0;
    cout << "Yes" << endl;
    return 0;
}

B - Dentist Aoki

Solution

按照題意模擬

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<int> p (n + 1, 1);
    for (int i = 1; i <= m; i++) {
        int x; cin >> x;
        p[x] ^= 1;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
        ans += p[i];
    cout << ans << endl;
    return 0;
}

C - Sort

Solution

記錄 \(A[i]\) 已經每個數對應的位置 \(p[x]\) ,顯然 \(pos[A[i]]=i\)

我們每次操作就是把第 \(i\) 小的數換到 \(i\) 位置

也就是位置 \(i\) 和位置 \(p[i]\)

交換之後更新 \(A\) 陣列和 \(p\) 陣列

Code

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
vector<pii> ans;
int main() {
    int n; cin >> n;
    vector<int> p(n + 1), a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        p[a[i]] = i;
    }
    for (int i = 1; i <= n; i++) {
        if (a[i] == i) continue;
        int pos = p[i];
        swap(a[pos], a[i]);
        ans.push_back({pos, i});
        p[a[pos]] = pos;
        p[a[i]] = i;
    }
    cout << ans.size() << endl;
    for (auto &x : ans) {
        if (x.first > x.second) 
            swap(x.first, x.second);
        cout << x.first << " " << x.second << endl;
    }
    return 0;
}

D - New Friends

Solution

顯然,透過任意此操作後,任何一個原來在同一連通塊的點直接都會有一條邊

所以,對於一個連通塊,需要新增的邊數就是 \(n\times (n-1)/2 -\) 已經存在的邊數,其中 \(n\) 為連通塊內點的個數

判斷連通塊可以用並查集來實現

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int main() {
    int N, M; cin >> N >> M;
    vector<vector<int> > g(N + 1);
    vector<pii> edges;
    for (int i = 1; i <= M; i++) {
        int A, B; cin >> A >> B;
        edges.push_back({A, B});
    }

    vector<int> fa(N + 1);
    iota(fa.begin(), fa.end(), 0);
    function<int(int)> find = [&](int x) {
        return fa[x] == x ? x : fa[x] = find(fa[x]);
    };

    for (auto &[A, B] : edges) {
        int x = find(A), y = find(B);
        if (x != y) fa[x] = y;
    }

    vector<int> cnt_x(N + 1, 0), cnt_y(N + 1, 0);
    for (int i = 1; i <= N; i++) 
        cnt_x[find(i)]++;
    for (auto &[A, B] : edges)
        cnt_y[find(A)]++;

    ll ans = 0;
    for (int i = 1; i <= N; i++) if (i == fa[i]) {
        ans += 1ll * cnt_x[i] * (cnt_x[i] - 1) / 2 - cnt_y[i];
    }
    cout << ans << endl;
    return 0;
}

E - Toward 0

Solution

一個比較顯然的機率 DP

定義 \(dp[i]\) 表示把 \(i\) 變成 \(0\) 的最小花費

第一種方法:

\[dp[i]=dp[\lfloor \frac{i}{A}\rfloor]+X \]

第二種方法:

\[dp[i]=\frac{1}{6}(dp[i] + dp[\lfloor \frac{i}{2}\rfloor]+\cdots+ dp[\lfloor \frac{i}{6}\rfloor]) +Y \]

移項得:

\[dp[i]=\frac{6}{5}\times (\frac{1}{6}\times(dp[\lfloor \frac{i}{2}\rfloor]+\cdots+ dp[\lfloor \frac{i}{6}\rfloor])+Y) \]

兩者中選小的轉移

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double ld;
ll N, A, X, Y; 

map<ll, ld> dp;
const ld p = 1 / 6.0;
ld f(ll T) {
    if (T == 0) return 0;
    if (dp.count(T)) return dp[T];
    ld ans1 = f(T / A) + X;
    ld ans2 = 1.2 * ( (f(T / 2) + f(T / 3) + f(T / 4) + f(T / 5) + f(T / 6)) * p + Y);
    return dp[T] = min(ans1, ans2);
}

int main() {
    cin >> N >> A >> X >> Y;
    printf ("%.10lf\n", f(N));
    return 0;
}

F - Transpose

Solution

我們可以把大小寫和翻轉作為兩種獨立的操作來考慮

對於大小寫,利用樹狀陣列記錄一個數字被改變大小寫了多少次,如果是偶數則不動,如果是奇數次則轉變大小寫

對於翻轉操作,可以用 Splay + 標記維護

Code

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

struct Node {
    int fa, ch[2], tag, siz;
};
vector<Node> t;
int rt;

bool ident (int x, int f) { // 判斷 x 是 f 的左兒子還是右兒子
    return t[f].ch[1] == x;
}

void connect (int x, int f, int op) {
    t[f].ch[op] = x;
    t[x].fa = f;
}

void push_up (int x) {
    t[x].siz = t[t[x].ch[0]].siz + t[t[x].ch[1]].siz + 1;
}

void push_down (int x) {
    if (t[x].tag) {
        swap (t[x].ch[0], t[x].ch[1]);
        t[t[x].ch[0]].tag ^= 1;
        t[t[x].ch[1]].tag ^= 1;
        t[x].tag = 0;
    }
}

void build (int l, int r, int f) {
    if (l > r) return ;
    int mid = (l + r) >> 1;
    if (mid < f) t[f].ch[0] = mid;
    else t[f].ch[1] = mid;
    t[mid].siz = 1; t[mid].fa = f;
    if (l == r) return ;
    build(l, mid - 1, mid); build(mid + 1, r, mid);
    push_up(mid);
}

void rotate (int x) {
    int f = t[x].fa, g = t[f].fa, k = ident(x, f);
    connect (x, g, ident(f, g));
    connect (t[x].ch[k ^ 1], f, k);
    connect (f, x, k ^ 1);
    push_up(f); push_up(x);
}

void splay (int x, int top) {
    if (!top) rt = x;
    while (t[x].fa != top) {
        int f = t[x].fa, g = t[f].fa;
        if (g != top)
            ident(x, f) ^ ident(f, g) ? rotate(x) : rotate(f);
        rotate(x);
    }
}

void rever (int L, int R) {
    splay(L, 0); splay(R, L);
    t[t[R].ch[0]].tag ^= 1;
}

int find (int x, int k) { // 找到以 x 為根的樹中第 k 個節點
    push_down(x);
    int sum = t[t[x].ch[0]].siz + 1;
    if (sum == k) return x;
    if (sum > k) return find(t[x].ch[0], k);
    else return find(t[x].ch[1], k - sum);
}

void inorder (int x, vector<int> & ord) {
    push_down(x);
    if (t[x].ch[0]) inorder(t[x].ch[0], ord);
    if (x >= 2 && x <= t.size() - 2)
        ord.push_back(x - 1);
    if (t[x].ch[1]) inorder(t[x].ch[1], ord);
}

vector<int> c;
void add_x (int x, int v) {
    for (int i = x; i < c.size(); i += i & -i)
        c[i] += v;
}
int query (int x) {
    int res = 0;
    for (int i = x; i; i -= i & -i)
        res += c[i];
    return res;
}

int main() {
    freopen ("F.in", "r", stdin);
    string s; cin >> s; s = " " + s;
    int n = s.size() - 1;
    t.resize(n + 3); c.resize(n + 1);
    rt = (n + 3) / 2; 
    build(1, rt - 1, rt); build(rt + 1, n + 2, rt);

    stack<int> st;
    for (int i = 1; i <= n; i++) {
        if (s[i] == '(') 
            st.push(i);
        else if (s[i] == ')'){
            int l = st.top(), r = i; st.pop();

            add_x(l, 1); add_x(r + 1, -1);

            int L = find (rt, l), R = find(rt, r + 2);
            rever(L, R);
        }
    }
    vector<int> ord;
    inorder(rt, ord);
    for (auto x : ord) {
        if (s[x] == '(' || s[x] == ')') continue;
        int v = query(x);
        if (v & 1) {
            if ('A' <= s[x] && s[x] <= 'Z') 
                s[x] = s[x] - 'A' + 'a';
            else 
                s[x] = s[x] - 'a' + 'A';
        }
        cout << s[x];
    }
    return 0;
}

G - Mediator

Solution

由於強制線上,沒法亂搞

我們記錄下每個節點的父親節點 \(fa[x]\),顯然,透過 \(fa[x]\) 我們可以得出詢問的答案

考慮如果維護 \(fa[x]\)

如果把 \(u\)\(v\) 連線起來

image.png

不妨設 \(u\)\(v\) 的父親

由於一棵樹的任意一個點當根節點仍然保持者樹的性質,所以我們讓 \(v\) 作為右邊那棵樹的根節點,然後把根節點接到 \(u\) 上面,完成合並

在轉換根節點的時候,\(v\) 所在的樹的 \(fa\) 也發生了改變,具體發生改變的點是 \(v\) 到原來根節點路徑上的所有點,改變方式是調換了位置,子節點和父節點發生了調換

這樣的調換最多出現 \(v\) 的子樹次,暴力修改會超時

想到啟發式合併,每次 \(v\) 作為較的子樹,\(u\) 作為較大的樹,所以總時間複雜度控制在 \(O(n\log n)\)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll TT = 998244353;

vector<int> fa, siz;

int find (int x) {
    if (fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

vector<int> real_fa;

int main() {
    freopen ("G.in", "r", stdin);
    int n, m; cin >> n >> m;
    fa.resize(n + 1); siz.resize(n + 1); real_fa.resize(n + 1);
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
        siz[i] = 1;
        real_fa[i] = 0;
    };


    int lst = 0;
    while (m--) {
        ll a, b, c; cin >> a >> b >> c;
        ll A = 1 + (((a * (1ll + lst)) % TT) % 2);
        ll B = 1 + (((b * (1ll + lst)) % TT) % n);
        ll C = 1 + (((c * (1ll + lst)) % TT) % n);
        
        int op = A, u = B, v = C;
        // int op, u, v; cin >> op >> u >> v;
        if (op == 1) {
            if (siz[find(u)] < siz[find(v)]) swap(u, v);
            siz[find(u)] += siz[find(v)]; fa[find(v)] = find(u);

            int nxt = real_fa[v]; real_fa[v] = u;
            for (int s = v, f = nxt; f;) {
                int nxt = real_fa[f];
                real_fa[f] = s;
                s = f; f = nxt;
            }
        }
        else if (op == 2) {
            if (find(u) != find(v)) {
                cout << (lst = 0) << '\n';
            }
            else {
                auto check = [&](int u, int v) {
                    if (u == real_fa[real_fa[v]]) return real_fa[v];
                    if (real_fa[u] == real_fa[v]) return real_fa[u];
                    return 0;
                };
                lst = check(u, v) | check(v, u);
                cout << lst << '\n';
            }
        }
    }
    return 0;
}

相關文章