C. King's Summit

设所有点中行坐标极差为 \(\Delta R\),列坐标极差为 \(\Delta C\),则最短相遇时间为 \(\max(\lceil \frac{\Delta R}{2} \rceil, \lceil \frac{\Delta C}{2} \rceil)\)

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

using namespace std;

int main() {
    int n;
    cin >> n;
    
    const int INF = 1001001001;
    int minR = INF, maxR = -INF;
    int minC = INF, maxC = -INF;
    rep(i, n) {
        int r, c;
        cin >> r >> c;
        minR = min(minR, r); maxR = max(maxR, r);
        minC = min(minC, c); maxC = max(maxC, c);
    }
    
    int ans = (max(maxR-minR, maxC-minC)+1)/2;
    cout << ans << '\n';
    
    return 0;
}

D. Substr Swap

只需要知道每个字符被交换次数的奇偶性,用差分计算区间和就能搞定!

代码实现
#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;
    string s, t;
    cin >> s >> t;
    
    vector<int> d(n+1);
    rep(i, m) {
        int l, r;
        cin >> l >> r;
        --l;
        d[l]++; d[r]--;
    }
    
    rep(i, n) d[i+1] += d[i];
    
    string ans;
    rep(i, n) {
        if (~d[i]&1) ans += s[i];
        else ans += t[i];
    }
    
    cout << ans << '\n';
    
    return 0;
}

E. Subarray Sum Divisibility

首先必须满足 \(A_1+A_2+\cdots + A_L\)\(M\) 的倍数。接着让 \(A_2+A_3+\cdots + A_{L+1}\) 也是 \(M\) 的倍数,由于这两个和的差等于 \(A_{L+1}-A_1\),所以 \(A_{L+1}\)\(A_1\) 关于模 \(M\) 必须同余才行!
同理可推出,关于模 \(L\) 同余下标的值,关于 \(M\) 也必须同余。因此,我们需要计算“将 \(A_k, A_{k+L}, A_{k+2L}, \cdots\)” 调整为模 \(M\) 等于 \(m\) 的最小操作次数,并记为 cost[k][m]

最后,为了让 \(A_1+A_2+\cdots + A_L\) 成为 \(M\) 的倍数,定义 dp[i][m]\(m_1 + m_2 + \cdots + m_i\)\(M\) 等于 \(m\) 时,\(\mathrm{cost}[1][m_1] + \mathrm{cost}[2][m_2] + \cdots + \mathrm{cost}[i][m_i]\) 的最小值进行 \(\mathrm{dp}\),最终答案就是 \(\mathrm{dp}[L][0]\)

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

using namespace std;

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

int main() {
    int n, m, l;
    cin >> n >> m >> l;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    
    vector cost(l, vector<int>(m));
    rep(i, n) {
        int g = i%l;
        rep(j, m) cost[g][(a[i]+j)%m] += j;
    }
    
    const int INF = 1001001001;
    vector<int> dp(m, INF);
    dp[0] = 0;
    rep(i, l) {
        vector<int> old(m, INF);
        swap(dp, old);
        rep(j, m)rep(k, m) {
            chmin(dp[(j+k)%m], old[j]+cost[i][k]);
        }
    }
    
    cout << dp[0] << '\n';
    
    return 0;
}

F. All Included

考虑向后添加字符构建字符串,可以设计出如下的 \(\mathrm{dp}\) 状态

dp[i][s][j] 表示长度为 \(i\) 的串中,包含的字符串集合为 \(s\),以字符串 \(j\) 结尾

其中 \(j\) 只需考虑“之后添加字符可能构成某个 \(S_i\)” 的情况,因此可能的状态不超过 \(8 \times 10\) 种。预处理好 \(j\) 的转移,\(\mathrm{dp}\) 的复杂度就是 \(\mathcal{O}(L \times 2^N \times 80 \times 26)\)

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

using namespace std;
using mint = modint998244353;

mint dp[101][1<<8][85];

int main() {
    int n, l;
    cin >> n >> l;
    
    vector<string> S(n);
    rep(i, n) cin >> S[i];
    
    map<string, int> id;
    rep(i, n) {
        rep(j, S[i].size()) {
            string prefix = S[i].substr(0, j+1);
            id[prefix] = 0;
        }
    }
    id[""] = 0;
    int m = 0;
    vector<string> sufs;
    for (auto& p : id) {
        p.second = m++;
        sufs.push_back(p.first);
    }
    vector<int> mask(m);
    rep(i, m) {
        string t = sufs[i];
        rep(j, n) {
            if (t.ends_with(S[j])) mask[i] |= 1<<j;
        }
    }
    vector to(m, vector<int>(26));
    rep(j, m) {
        rep(c, 26) {
            string t = sufs[j]; t += 'a'+c;
            while (!id.count(t)) t.erase(t.begin());
            to[j][c] = id[t];
        }
    } 
    
    int n2 = 1<<n;
    dp[0][0][0] = 1;
    rep(i, l)rep(s, n2)rep(j, m) {
        mint now = dp[i][s][j];
        if (now == 0) continue;
        rep(c, 26) {
            int ni = i+1;
            int nj = to[j][c];
            int ns = s | mask[nj];
            dp[ni][ns][nj] += now;
        }
    }
    
    mint ans;
    rep(j, m) ans += dp[l][n2-1][j];
    
    cout << ans.val() << '\n';
    
    return 0;
}

其实也可以ac自动机上dp,好像过的人基本写的都是这个,所以应该不需要讲了吧

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

using namespace std;
using mint = modint998244353;

struct Aho {
    bool inited;
    using MP = map<char, int>;
    vector<MP> to;
    vector<int> cnt, fail;
    Aho(): to(1), cnt(1) {}
    int add(const string& s) {
        int v = 0;
        for (char c : s) {
            if (!to[v].count(c)) {
                to[v][c] = to.size();
                to.push_back(MP());
                cnt.push_back(0);
            }
            v = to[v][c];
        }
        cnt[v]++;
        return v;
    }
    
    vector<int> vs;
    void init() {
        fail = vector<int>(to.size(), -1);
        queue<int> q;
        q.push(0);
        while (q.size()) {
            int v = q.front(); q.pop();
            vs.push_back(v);
            for (auto [c, u] : to[v]) {
                fail[u] = (*this)(fail[v], c);
                cnt[u] += cnt[fail[u]];
                q.push(u);
            }
        }
    }
    int operator()(int v, char c) const {
        while (v != -1) {
            auto it = to[v].find(c);
            if (it != to[v].end()) return it->second;
            v = fail[v];
        }
        return 0;
    }
    int operator[](int v) const { return cnt[v]; }
};

mint dp[101][85];

int main() {
    int n, l;
    cin >> n >> l;
    
    vector<string> S(n);
    rep(i, n) cin >> S[i];
    
    mint ans;
    rep(s, 1<<n) {
        Aho aho;
        rep(i, n) if (s>>i&1) aho.add(S[i]);
        aho.init();
        int m = aho.to.size();
        
        rep(i, l+1)rep(j, m) dp[i][j] = 0;
        dp[0][0] = 1;
        rep(i, l)rep(j, m) {
            mint now = dp[i][j];
            if (now == 0) continue;
            rep(c, 26) {
                int ni = i+1;
                int nj = aho(j, 'a'+c);
                if (!aho[nj]) dp[ni][nj] += now;
            }
        }
        
        mint now;
        rep(j, m) now += dp[l][j];
        ans += __builtin_parity(s) ? -now : now;
    }
    
    cout << ans.val() << '\n';
    
    return 0;
}

G. Count Simple Paths 2

不断删除度数为 \(1\) 的点,缩合度数为 \(2\) 的点,然后直接 \(\mathrm{dfs}\) 就能过!

这题的难度主要在于建虚树的图

代码实现
#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<vector<int>> to(n);
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
        to[b].push_back(a);
    }
    
    vector<int> deg(n);
    vector<bool> live(n, true);
    {
        rep(i, n) deg[i] = to[i].size();
        queue<int> q;
        for (int i = 1; i < n-1; ++i) if (deg[i] == 1) q.push(i);
        while (q.size()) {
            int v = q.front(); q.pop();
            live[v] = false;
            for (int u : to[v]) {
                deg[u]--;
                if (deg[u] == 1 and u != 0 and u != n-1) q.push(u);
            }
        }
    }
    
    int nn;
    vector<vector<P>> g;
    {
        vector<int> vs;
        rep(i, n) if (live[i] and (deg[i] > 2 or i == 0 or i == n-1)) vs.push_back(i);
        vector<int> vid(n, -1);
        
        nn = vs.size();
        rep(i, nn) vid[vs[i]] = i;
        
        g.resize(nn);
        for (int v : vs) {
            for (int u : to[v]) if (live[u]) {
                int len = 1, pre = v;
                while (vid[u] == -1) {
                    int nu = -1;
                    for (int w : to[u]) {
                        if (w == pre) continue;
                        if (!live[w]) continue;
                        nu = w;
                    }
                    pre = u;
                    u = nu;
                    len++;
                }
                g[vid[v]].emplace_back(vid[u], len);
            }
        }
    }
    
    vector<int> ans(n);
    vector<bool> used(nn);
    auto f = [&](auto& f, int v, int l) {
        if (v == nn-1) {
            ans[l]++;
            return;
        }
        used[v] = true;
        for (auto [u, len] : g[v]) {
            if (used[u]) continue;
            f(f, u, l+len);
        }
        used[v] = false;
    };
    f(f, 0, 0);
    
    rep(i, n) if (i) cout << ans[i] << ' ';
    
    return 0;
}