C. K-bonacci

前缀和

代码实现
#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 = modint;

int main() {
    int n, k;
    cin >> n >> k;
    
    vector<mint> a(max(k, n+1));
    vector<mint> s(a.size()+1);
    
    rep(i, k) a[i] = 1;
    rep(i, k) s[i+1] = s[i]+a[i];
    
    mint::set_mod(1e9);
    for (int i = k; i <= n; ++i) {
        a[i]= s[i] - s[i-k];
        s[i+1] = s[i]+a[i];
    }
    
    cout << a[n].val() << '\n';

    return 0;
}

还有另一种做法
\(a_n = 2_{n-1} - a_{n-k-1}\)

代码实现
#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 = modint;

int main() {
    int n, k;
    cin >> n >> k;
    
    vector<mint> a(max(k, n+1));
    rep(i, k) a[i] = 1;
    a[k] = k;
    
    mint::set_mod(1e9);
    for (int i = k+1; i <= n; ++i) {
        a[i]= 2*a[i-1] - a[i-k-1];
    }
    
    cout << a[n].val() << '\n';

    return 0;
}

D. Logical Filling

如果 o 旁边有 ?,这个 ? 只能改成 .
然后进行分类讨论!
如果 \(S\)o 的数量等于 \(k\) 时,那么所有的 ? 只能替换为 .
如果不同,则只有在需要将 o 紧密排列时,o 的位置才会确定,因此需要计算最多可以增加多少个 o,如果这个数量恰好等于 \(k\) 时,就在 ? 连续出现奇数个的地方紧密排列 o

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

using namespace std;

int main() {
    int n, k;
    string s;
    cin >> n >> k >> s;
    
    rep(i, n) {
        if (s[i] == 'o') {
            if (i) s[i-1] = '.';
            if (i+1 < n) s[i+1] = '.';
        }
    }
    
    int x = k - ranges::count(s, 'o');
    vector<pair<int, int>> ps;
    {
        int i = 0;
        while (i < n) {
            if (s[i] == '?') {
                int l = i;
                while (i < n and s[i] == '?') i++;
                int r = i;
                ps.emplace_back(l, r);
            }
            else i++;
        }
    }
    int mx = 0;
    for (auto [l, r] : ps) mx += (r-l+1)/2;
    
    if (x == 0) {
        for (auto [l, r] : ps) {
            for (int i = l; i < r; ++i) s[i] = '.';
        }
    }
    else if (x == mx) {
        for (auto [l, r] : ps) {
            if ((r-l)%2 == 0) continue;
            rep(i, r-l) {
                s[l+i] = "o."[i%2];
            }
        }
    }
    
    cout << s << '\n';

    return 0;
}

E. Reachable Set

首先,当删除所有大于 \(k\) 的点时,如果无法到达 \(\leqslant k\) 的点,那么答案是 \(-1\)
如果不是,那么所有直接连接到 \(\leqslant k\) 的点的那些点都必须全部删除,删除后 \(\leqslant k\) 的点和大于 \(k\) 的点将不再连通,这样就满足条件了。
这两个条件可以按照 \(k\) 的顺序分别判断!

代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#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);
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
        to[b].push_back(a);
    }
    
    dsu uf(n);
    vector<bool> red(n); int cnt = 0;
    rep(v, n) {
        for (int u : to[v]) {
            if (u < v) uf.merge(u, v);
            if (u > v) {
                if (red[u]) continue;
                red[u] = true;
                cnt++;
            }
        }
        if (red[v]) cnt--;
        
        int ans = -1;
        if (uf.size(v) == v+1) ans = cnt;
        cout << ans << '\n';
    }

    return 0;
}

F. Add One Edge 3

需要考虑树的直径!
将两树合并后得到的新树的直径要么是经过新添加的边,要么是原来某颗树的直径。
在原树中,每个点到最远点的距离分别记为 \(D1_i\)\(D2_i\),那么 \(f(i, j)\) 就是 \(\max(树1的直径,树2的直径,D1_i + D2_j+1)\)
由于最远点是直径的两端点之一,因此 \(D1_i\)\(D2_i\) 都可以在 \(O(N)\) 时间全部求出来。
计算 \(\sum\sum f(i, j)\) 可以使用类似双指针的方法,或者 \(\operatorname{fft}\) 来实现!

代码实现
#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 ll = long long;
using P = pair<int, int>;

int main() {
    auto readTree = [&]() {
        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);
        }
        
        vector<int> dist(n);
        auto dfs = [&](auto& f, int v, int dep=0, int p=-1) -> P {
            dist[v] = max(dist[v], dep);
            P res(dep, v);
            for (int u : to[v]) {
                if (u == p) continue;
                res = max(res, f(f, u, dep+1, v));
            }
            return res;
        };
        int a = dfs(dfs, 0).second;
        auto [d, b] = dfs(dfs, a);
        dfs(dfs, b);
        
        vector<ll> c(n);
        rep(i, n) c[dist[i]]++;
        return make_pair(d, c);
    };
    
    auto [da, a] = readTree();
    auto [db, b] = readTree();
    int d = max(da, db);
    
    auto c = convolution_ll(a, b);
    ll ans = 0;
    rep(i, c.size()) {
        ans += max(i+1, d)*c[i];
    }
    
    cout << ans << '\n';
    
    return 0;
}

G. Push Simultaneously

二分答案
考虑“能否在 \(t\) 秒内完成”的判定问题,可以转化为二分图匹配问题!也就是判定二分图最大匹配是否是完美匹配。

代码实现
#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 ll = long long;

int main() {
    int n;
    cin >> n;
    
    vector<pair<ll, ll>> s, t;
    rep(i, n) {
        ll x, y;
        cin >> x >> y;
        s.emplace_back(x, y);
    }
    rep(i, n) {
        ll x, y;
        cin >> x >> y;
        t.emplace_back(x, y);
    }
    
    vector dist(n, vector<double>(n));
    vector<double> dists;
    rep(i, n)rep(j, n) {
        auto [sx, sy] = s[i];
        auto [tx, ty] = t[j];
        dist[i][j] = hypot(sx-tx, sy-ty);
        dists.push_back(dist[i][j]);
    }
    ranges::sort(dists);
    
    auto judge = [&](double x) {
        int sv = n*2, tv = sv+1;
        mf_graph<int> g(tv+1);
        rep(i, n) g.add_edge(sv, i, 1);
        rep(i, n) g.add_edge(n+i, tv, 1);
        rep(i, n)rep(j, n) if (dist[i][j] <= x) {
            g.add_edge(i, n+j, 1);
        }
        return g.flow(sv, tv) == n;
    };
    
    int ac = dists.size()-1, wa = -1;
    while (ac-wa > 1) {
        int wj = (ac+wa)/2;
        (judge(dists[wj]) ? ac : wa) = wj; 
    }
    
    printf("%.10f\n", dists[ac]);
    
    return 0;
}