A. Who Ate the Cake?

模拟

代码实现
a, b = map(int, input().split())
if a == b:
    print(-1)
else:
    print(6-a-b)

B. Piano 2

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(n), b(m);
    rep(i, n) cin >> a[i];
    rep(i, m) cin >> b[i];
    
    vector<P> ps;
    rep(i, n) ps.emplace_back(a[i], 0);
    rep(i, m) ps.emplace_back(b[i], 1);
    
    sort(ps.begin(), ps.end());
    
    rep(i, n+m-1) {
        if (ps[i].second == 0 and ps[i+1].second == 0) {
            puts("Yes");
            return 0;
        }
    }
    
    puts("No");
    
    return 0;
}

C. Bingo 2

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n, t;
    cin >> n >> t;
    
    vector<int> row(n), col(n);
    int dia1 = 0, dia2 = 0;
    
    rep(ti, t) {
        int a;
        cin >> a;
        --a;
        int i = a/n, j = a%n;
        
        auto add = [&](int& x) {
            x++;
            if (x == n) {
                cout << ti+1 << '\n';
                exit(0);
            }
        };
        
        add(row[i]); 
        add(col[j]);
        if (i == j) add(dia1);
        if (i+j == n-1) add(dia2);
    }
    
    puts("-1");
    
    return 0;
}

D. Intersecting Intervals

可以用总方案数容斥掉不相交的区间的个数
什么时候两区间会不相交?一个区间的右端点小于另一个区间的左端点
那么我们可以像B题那样对区间左右两端点分别标记为 \(0\)\(1\),然后放在一起排序,接着遍历所有二元组 \((x, t)\),当 \(t=0\) 时,将答案减去前面右端点的个数,当 \(t=1\) 时,将右端点个数 +1

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;
using ll = long long;

int main() {
    int n;
    cin >> n;
    
    vector<P> ps;
    rep(i, n) {
        int l, r;
        cin >> l >> r;
        ps.emplace_back(l, 0);
        ps.emplace_back(r, 1);
    }
    
    sort(ps.begin(), ps.end());
    
    ll ans = (ll)n*(n-1)/2;
    int rs = 0;
    for (auto [x, t] : ps) {
        if (t == 0) ans -= rs;
        else rs++;
    }
    
    cout << ans << '\n';
    
    return 0;
}

E. Guess the Sum

为了方便处理,不妨将 \(R\) 加上 \(1\)
\(S_i = \sum\limits_{j = 0}^{i-1} a_j\)
注意到 \(S_R - S_L = (-1)\times S_R + 1 \times S_L\),同样对于 \(S_r - S_l\) 也可以表示成 \((-1)\times S_l + 1 \times S_r\)
假设从当前点向右边连边为正贡献,向左连边为负贡献,分别用 1-1 来标记
那么 \(S_R - S_L\) 就可以由从 \(L\) 点出发经过若干条边走到 \(R\) 点的最短路得到,关于如何求最短路,可以从当前点向左右两边进行扩展,也就是 \(v - 2^i\) 以及 \(v+2^i\),直到扩展到 \(v\) 点在二进制下末尾第一个 \(1\) 的位置

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using P = pair<int, int>;
using ll = long long;

int main() {
    int n, l, r;
    cin >> n >> l >> r;
    int n2 = 1<<n;
    r++;
    
    const int INF = 1001001001;
    vector<int> dist(n2+1, INF), pre(n2+1, -1);
    queue<int> q;
    dist[l] = 0; q.push(l);
    while (q.size()) {
        int v = q.front(); q.pop();
        auto push = [&](int to) {
            if (to < 0 or to > n2) return;
            if (dist[to] != INF) return;
            dist[to] = dist[v]+1;
            pre[to] = v;
            q.push(to);
        };
        rep(i, n+1) {
            push(v-(1<<i));
            push(v+(1<<i));
            if (v>>i&1) break;
        }
    }
    
    int ans = 0;
    auto query = [&](int s, int t) {
        int sign = 1;
        if (s > t) swap(s, t), sign = -1;
        {
            int i = 0, j = s, w = t-s;
            while (w%2 == 0) j >>= 1, i++, w >>= 1;
            cout << "? " << i << ' ' << j << '\n';
        }
        int x;
        cin >> x;
        ans = (ans + x*sign + 100)%100; 
    };
    while (r != l) {
        query(pre[r], r);
        r = pre[r];
    }
    cout << "! " << ans << '\n';
    
    return 0;
}

F. MST Query

考虑图 \(G\) 的子图,由 \(G\) 上边权大小不超过 \(k\) 的边生成的子图 \(G_k (0 \leqslant k \leqslant 10)\)。记图 \(H\) 中的连通分量的个数为 \(c(H)\),那么图 \(G\) 上的最小生成树上的所有边的权值总和等价于 \(\sum\limits_{k=0}^9 (c(G_k)-1)\)

证明:对于图 \(G\) 的最小生成树上的边,边权不超过 \(k\) 的边有 \(N-c(G_k)\)。所以,最小生成树上边权恰好为 \(k\) 的边数就是 \((N-c(G_k)) - (N-c(G_{k-1})) = c(G_{k-1}) - c(G_k)\)。于是,最小生成树上的边权和就是 \(\sum\limits_{k=1}^{10} k \cdot (c(G_{k-1}) - c(G_k)) = \sum\limits_{k=0}^9 c(G_k) - 10c(G_{10}) = \sum\limits_{k=0}^9 (c(G_k)-1)\),其中 \(G_{10}\) 就是图 \(G\),所以连通分量的个数为 \(1\)

代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n, q;
    cin >> n >> q;
    
    int m = 10;
    int ans = (n-1)*m;
    vector<dsu> uf(m, dsu(n));
    rep(qi, n-1+q) {
        int a, b, c;
        cin >> a >> b >> c;
        --a; --b;
        for (int i = c; i < m; ++i)  {
            if (uf[i].same(a, b)) continue;
            ans--;
            uf[i].merge(a, b);
        }
        if (qi < n-1) continue;
        
        cout << ans << '\n';
    } 
    
    return 0;
}