T1:Chord

模拟

代码实现
s = input()
if s in 'ACE, BDF, CEG, DFA, EGB, FAC, GBD':
    print('Yes')
else:
    print('No')

T2:TaK Code

模拟

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

using namespace std;

bool check(vector<string> s) {
    rep(i, 3)rep(j, 3) if (s[i][j] != '#') return false;
    rep(i, 3)rep(j, 3) if (s[8-i][8-j] != '#') return false;
    rep(i, 4)rep(j, 4) {
        if (i < 3 and j < 3) continue;
        if (s[i][j] != '.') return false;
        if (s[8-i][8-j] != '.') return false;
    }
    return true;
}

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<string> s(n);
    rep(i, n) cin >> s[i];
    
    rep(si, n-8)rep(sj, m-8) {
        vector<string> t(9);
        rep(i, 9)rep(j, 9) t[i] += s[si+i][sj+j];
        if (check(t)) cout << si+1 << ' ' << sj+1 << '\n';
    }
    
    return 0;
}

T3:Invisible Hand

二分答案

代码实现
#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<int> a(n), b(m);
    rep(i, n) cin >> a[i];
    rep(i, m) cin >> b[i];
    
    int wa = 0, ac = 1001001001;
    while (ac-wa > 1) {
        int wj = (ac+wa)/2;
        int na = 0, nb = 0;
        rep(i, n) if (a[i] <= wj) na++;
        rep(i, m) if (b[i] >= wj) nb++;
        if (na >= nb) ac = wj; else wa = wj;
    }
    
    cout << ac << '\n';
    
    return 0;
}

T4:Count Bracket Sequences

( 看成 +1,将 ) 看成 -1,合法括号序列的前提条件是要保证任意位置处的前缀和 \(\geqslant 0\)

然后暴力 \(\operatorname{dp}\) 即可

dp[i][j] 表示到第 \(i\) 个字符为止的前缀和为 \(j\) 的合法方案数
最后的答案为 \(\operatorname{dp}[n][0]\)

时间复杂度为 \(\mathcal{O}(n^2)\)

代码实现
#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;
using mint = modint998244353;

int main() {
    string s;
    cin >> s;
    int n = s.size();
    
    vector dp(n+1, vector<mint>(n+1));
    dp[0][0] = 1;
    rep(i, n) {
        rep(j, n) {
            if (s[i] != ')') dp[i+1][j+1] += dp[i][j];
            if (s[i] != '(' and j > 0) dp[i+1][j-1] += dp[i][j];
        }
    }
    
    cout << dp[n][0].val() << '\n';
    
    return 0;
}

T5:Tangency of Cuboids

由于用平面将长方体切开后前后块互不干扰,所以可以用 set 来做合并

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

using namespace std;

const int M = 101;
int id[M][M][M];

int main() {
    int n;
    cin >> n;
    
    rep(i, M)rep(j, M)rep(k, M) id[i][j][k] = -1;
    rep(i, n) {
        int lx, ly, lz; cin >> lx >> ly >> lz;
        int rx, ry, rz; cin >> rx >> ry >> rz;
        for (int x = lx; x < rx; ++x) {
            for (int y = ly; y < ry; ++y) {
                for (int z = lz; z < rz; ++z) {
                    id[x][y][z] = i;
                }
            }
        }
    }
    
    vector<unordered_set<int>> st(n);
    auto add = [&](int a, int b) {
        if (a == -1 or b == -1) return;
        if (a == b) return;
        st[a].insert(b);
        st[b].insert(a);
    };
    rep(x, M-1)rep(y, M-1)rep(z, M-1) {
        add(id[x][y][z], id[x][y][z+1]);
        add(id[x][y][z], id[x][y+1][z]);
        add(id[x][y][z], id[x+1][y][z]);
    }
    
    rep(i, n) cout << st[i].size() << '\n';
    
    return 0;
}

T6:Cans and Openers

如果不考虑不用开罐器就能打开的罐头,那么只需重复购买剩下可使用次数最多的开罐器,然后按满意度从高到低依次购买罐头,就能找到购买 \(0 \sim m\) 件商品的答案。

接下来,只需枚举所有不用开罐器就能打开的罐头数量,把它们加起来即可!

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

using namespace std;
using ll = long long;

inline void chmin(int& a, int b) { if (a > b) a = b; }

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<ll> a, b, c;
    rep(i, n) {
        int t, x;
        cin >> t >> x;
        if (t == 0) a.push_back(x);
        if (t == 1) b.push_back(x);
        if (t == 2) c.push_back(x);
    }
    
    auto f = [&](vector<ll>& a) {
        sort(a.rbegin(), a.rend());
        a.resize(m);
        a.insert(a.begin(), 0);
        rep(i, a.size()-1) a[i+1] += a[i];
    };
    f(a);
    sort(b.rbegin(), b.rend()); b.resize(m);
    sort(c.rbegin(), c.rend()); c.resize(m);
    
    ll ans = a[m];
    int num = 0, bi = 0, ci = 0;
    ll now = 0;
    rep(i, m) {
        if (num) num--, now += b[bi++];
        else num += c[ci++];
        ans = max(ans, now+a[m-i-1]);
    }
    
    cout << ans << '\n';
    
    return 0;
}

T7:Avoid Straight Line

正难则反

我们可以考虑统计三点落在同一条树链上的方案数,答案为 \(\frac{1}{2}\left(\sum\limits_{u \neq v} \big(\operatorname{dist}(u, v)-1\big)\right)\) ,其中 \(\operatorname{dist}(u, v)\) 表示点 \(u\) 到点 \(v\) 的最短路

\( \sum\limits_{u \neq v} \big(\operatorname{dist}(u, v)-1\big) = \sum\limits_{u \neq v} \operatorname{dist}(u, v) - \sum\limits_{u \neq v} 1 \)

\( \frac{1}{2}\sum\limits_{u \neq v} \operatorname{dist}(u, v) = \sum\limits_e \text{经过边}e\text{的树链}(u, v)\text{的数量} \),其实就是这道经典题Tree Distance(★5)题解

\(\frac{1}{2}\sum\limits_{u \neq v} 1 = {_n}C_2\)

代码实现
#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;
    cin >> n;
    
    vector<vector<int>> to(n);
    rep(i, n-1) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
        to[b].push_back(a);
    }
    
    ll ans = (ll)n*(n-1)*(n-2)/6 + (ll)n*(n-1)/2;
    auto dfs = [&](auto& f, int v, int p=-1) -> int {
        int t = 1;
        for (int u : to[v]) {
            if (u == p) continue;
            int r = f(f, u, v);
            ans -= (ll)r*(n-r);
            t += r;
        }
        return t;
    };
    dfs(dfs, 0);
    
    cout << ans << '\n';
    
    return 0;
}

T8:snukesnuke

容易发现对于最小周期相同的字符串的形式都是 "*" \(\times x\)

\(i\) 个人的昵称就是从上一个最小周期和 \(S_i\) 一样且循环次数 \(x\) 也一样的昵称的 \(k\)+1 开始检查
可以用哈希表来完成记忆化

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

using namespace std;
using ll = long long;

// Morris-Pratt
template<typename T>
struct MP {
    int n;
    T t;
    vector<int> a;
    MP() {}
    MP(const T& t): t(t) {
        n = t.size();
        a = vector<int>(n+1);
        a[0] = -1;
        int j = -1;
        rep(i, n) {
            while (j != -1 and t[j] != t[i]) j = a[j];
            j++;
            a[i+1] = j;
        }
    }
    int operator[](int i) { return a[i]; }
    vector<int> findAll(const T& s) {
        vector<int> res;
        int j = 0;
        for (int i = 0; i < s.size(); ++i) {
            while (j != -1 and t[j] != s[i]) j = a[j];
            j++;
            if (j == n) {
                res.push_back(i-j+1);
                j = a[j];
            }
        }
        return res;
    }
};

struct Solver {
    unordered_set<int> s;
    unordered_map<int, int> lk;
    Solver() {}
    int operator()(int n) {
        int k = lk[n]+1;
        while (s.count(k*n)) k++;
        s.insert(k*n);
        lk[n] = k;
        return k;
    }
};

int main() {
    int q;
    cin >> q;
    
    map<string, Solver> ss;
    rep(qi, q) {
        string s;
        cin >> s;
        int n = s.size();
        int c = n;
        {
            MP mp(s);
            c = n-mp[n];
            if (n%c) c = n;
        }
        s = s.substr(0, c);
        n /= c;
        cout << ss[s](n) << ' ';
    }
    
    return 0;
}