T1:Attack

答案为 \(\lceil\frac{A}{B}\rceil\)

代码实现
a, b = map(int, input().split())
print((a+b-1)//b)

T2:Find snuke

爆搜

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

using namespace std;

int di[] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dj[] = {-1, 0, 1, -1, 1, -1, 0, 1};

int main() {
    int h, w;
    cin >> h >> w;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    
    string T = "snuke";
    rep(si, h)rep(sj, w) {
        rep(v, 8) {
            int i = si, j = sj;
            rep(k, 5) {
                if (i < 0 or j < 0 or i >= h or j >= w) break;
                if (s[i][j] != T[k]) break;
                if (k == 4) {
                    i = si; j = sj;
                    rep(nk, 5) {
                        cout << i+1 << ' ' << j+1 << '\n';
                        i += di[v]; j += dj[v];
                    }
                    return 0;
                }
                i += di[v]; j += dj[v];
            }
        }
    }
    
    return 0;
}

T3:Almost Equal

爆搜

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

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<string> s(n);
    rep(i, n) cin >> s[i];
    
    sort(s.begin(), s.end());
    
    do {
        bool ok = true;
        rep(i, n-1) {
            int d = 0;
            rep(j, m) if (s[i][j] != s[i+1][j]) d++;
            if (d != 1) ok = false;
        }
        if (ok) {
            puts("Yes");
            return 0;
        }
    } while (next_permutation(s.begin(), s.end()));
    
    puts("No");
    
    return 0;
}

T4:Impartial Gift

双指针或者二分
枚举给青木的礼物 \(x\),给snuke的礼物就选价值范围为 \([x-d, x+d]\) 中价值最大的礼物

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

using namespace std;
using ll = long long;

int main() {
    int n, m; ll d;
    cin >> n >> m >> d;
    
    vector<ll> a(n), b(m);
    rep(i, n) cin >> a[i];
    rep(i, m) cin >> b[i];
    
    sort(a.begin(), a.end());
    sort(b.begin(), b.end());
    
    ll ans = -1;
    rep(i, n) {
        ll l = a[i]-d, r = a[i]+d;
        int j = upper_bound(b.begin(), b.end(), r)-b.begin();
        if (j > 0) {
            ll x = b[j-1];
            if (l <= x) {
                ans = max(ans, a[i]+x);
            }
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

T5:Isolation

可以用 std::unordered_set 来维护邻接表
一开始的答案为 \(n\),然后考虑每次的操作对答案的影响即可

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

using namespace std;
using ll = long long;

int main() {
    int n, q;
    cin >> n >> q;
    
    vector<unordered_set<int>> to(n);
    int ans = n;
    rep(qi, q) {
        int type;
        cin >> type;
        if (type == 1) {
            int a, b;
            cin >> a >> b;
            --a; --b;
            if (to[a].size() == 0) ans--;
            if (to[b].size() == 0) ans--;
            to[a].insert(b);
            to[b].insert(a);
        }
        else {
            int v;
            cin >> v;
            --v;
            if (to[v].size()) {
                for (int u : to[v]) {
                    to[u].erase(v);
                    if (to[u].size() == 0) ans++;
                }
                to[v].clear();
                ans++;
            }
        }
        cout << ans << '\n';
    }
    
    return 0;
}

T6:Merge Set

对任意两个有公共元素的集合进行连边,进行一次操作就相当于经过一条边,那么在这个图上从 \(1\) 走到 \(M\) 的最短路就是答案。

但这样显然会超时

优化方法:可以将每个集合 \(S_i\) 视为中转点,将 \(S_i\) 里每个数都和这个中转点进行连边,这样一来对每个集合作图的时间就是 \(O(A_i)\)

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

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> to(n+m);
    rep(i, n) {
        int a;
        cin >> a;
        rep(j, a) {
            int x;
            cin >> x;
            --x;
            to[x].push_back(m+i);
            to[m+i].push_back(x);
        }
    }
    
    const int INF = 1001001001;
    vector<int> dist(n+m, INF);
    queue<int> q;
    dist[0] = 0; q.push(0);
    while (q.size()) {
        int v = q.front(); q.pop();
        for (int u : to[v]) {
            if (dist[u] != INF) continue;
            dist[u] = dist[v]+1;
            q.push(u);
        }
    }
    
    int ans = dist[m-1];
    if (ans == INF) ans = -1;
    else ans = (ans-2)/2;
    cout << ans << '\n';
    
    return 0;
}

T7:Sort from 1 to 4

假设将 \(A\) 做升序排序后的序列是 \(B\),考虑从 \(A_i\)\(B_i\) 连一条边,那么我们可以得到一个 \(4\) 个点 \(N\) 条边的有向图。

结论:答案为 \(N -\) 图上的环数

为了使得答案最大,我们应该尽可能地使环数变多。首先,自环和 \(2\) 元环是固定不变的,\(3\) 元环和 \(4\) 元环可以放在一起考虑

在上图中,\(4\) 元环中可能包含 \(2\)\(3\) 元环,通过改变这 \(4\) 个点的位置可以使其中包含的 \(3\) 元环的个数尽可能的多

假设 \(x_3\)\(3\) 元环的个数,\(x_4\)\(4\) 元环的个数,\(es\)\(3\) 元环和 \(4\) 元环的总边数,那么 \(es=3x_3+4x_4\)
我们一旦求出了 \(x_3\),就能通过 \(x_4 = \frac{es-3x_3}{4}\) 求出 \(x_4\)

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

using namespace std;

int main() {
    int n;
    cin >> n;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    rep(i, n) a[i]--;
    auto b = a;
    sort(b.begin(), b.end());
    
    int m = 4;
    vector g(m, vector<int>(m));
    rep(i, n) g[a[i]][b[i]]++;
    
    int ans = 0;
    rep(i, m) {
        ans += g[i][i];
        g[i][i] = 0;
    }
    rep(i, m)rep(j, i) {
        int c = min(g[i][j], g[j][i]);
        ans += c;
        g[i][j] -= c;
        g[j][i] -= c;
    }
    
    {
        int es = 0;
        rep(i, m)rep(j, m) es += g[i][j];
        vector<int> p(m);
        iota(p.begin(), p.end(), 0);
        int x3 = 0;
        do {
            int now = 0;
            int a = g[p[2]][p[1]];
            int b = min(g[p[1]][p[0]], g[p[0]][p[2]]);
            int c = min(g[p[1]][p[3]], g[p[3]][p[2]]);
            now = min(a, b+c);
            x3 = max(x3, now);
        } while (next_permutation(p.begin(), p.end()));
        int x4 = (es-x3*3)/4;
        ans += x3+x4;
    }
    
    cout << n-ans << '\n';
     
    return 0;
}