C. Equilateral Triangle

注意到等边三角形的每条边对应的弧一定也相等,那么如果圆的周长 \(L\) 不是 \(3\) 的倍数的话,就一定无解

\(r = \dfrac{L}{3}\)
固定点 \(x_a\),它可以和点 \(x_a+r\) 以及点 \(x_a+2r\) 构成等边三角形。然后利用乘法原理来算贡献即可
注意最后需要对答案除以 \(3\)

代码实现
#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, L;
    cin >> n >> L;
    
    vector<int> x(n);
    rep(i, n-1) {
        int d;
        cin >> d;
        x[i+1] = (x[i]+d)%L;
    }
    
    vector<int> cnt(L);
    rep(i, n) cnt[x[i]]++;
    
    if (L%3 != 0) {
        puts("0");
        return 0;
    }
    int r = L/3;
    
    ll ans = 0;
    rep(a, n) {
        int x1 = (x[a]+r)%L;
        int x2 = (x1+r)%L;
        ans += (ll)cnt[x1]*cnt[x2];
    }
    ans /= 3;
    
    cout << ans << '\n';
    
    return 0;
}

D. String Rotation

为了保证字典序最小,所选区间的左端点一定是字符串最左边满足 \(S_i > S_{i+1}\)\(i\),不妨记为 \(l\)
接下来我们要找区间右端点 \(r\),这其实等价于一直交换 \(S_l\) 和它右边的相邻字符,直到满足 \(S_l\) 小于它右边的相邻字符,这时就可以停下来了,此时 \(r\) 就是 当前 \(S_l\) 所在的位置

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

using namespace std;

void solve() {
    int n;
    string s;
    cin >> n >> s;
    
    int l = -1;
    rep(i, n-1) {
        if (s[i] > s[i+1]) {
            l = i;
            break;
        }
    }
    if (l != -1) {
        for (int i = l; i < n-1; ++i) {
            if (s[i] < s[i+1]) break;
            swap(s[i], s[i+1]);
        }
    }
    
    cout << s << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

E. Pair Annihilation

容易发现将树上所有电子归并到树上任意一点的费用都是一样的
不妨取点 \(1\) 为根节点,我们可以将所有电子移动到根节点上
具体地,自底向上,将当前点上的电子转移给它的父节点

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

using namespace std;
using ll = long long;

struct Edge { // C++20特性,不需要写构造函数
    int to, cost;
};

int main() {
    int n;
    cin >> n;
    
    vector<int> x(n);
    rep(i, n) cin >> x[i];
    
    vector<vector<Edge>> g(n);
    rep(i, n-1) {
        int u, v, w;
        cin >> u >> v >> w;
        --u; --v;
        g[u].emplace_back(v, w);
        g[v].emplace_back(u, w);
    }
    
    ll ans = 0;
    auto dfs = [&](auto& f, int v, int p=-1) -> int {
        int tot = x[v];
        for (auto [u, w] : g[v]) {
            if (u == p) continue;
            int r = f(f, u, v);
            ans += (ll)w*abs(r);
            tot += r;
        }
        return tot;
    }; 
    dfs(dfs, 0);
    
    cout << ans << '\n';
    
    return 0;
}

F. Connecting Points

用小根堆和并查集模拟即可

代码实现
#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, q;
    cin >> n >> q;
    
    using P = pair<int, int>;
    using Edge = pair<int, P>;
    priority_queue<Edge, vector<Edge>, greater<Edge>> pq;
    
    vector<int> x, y;
    auto add = [&](int nx, int ny) {
        int v = x.size();
        rep(i, x.size()) {
            int dist = abs(nx-x[i]) + abs(ny-y[i]);
            pq.emplace(dist, P(i, v));
        }
        x.push_back(nx);
        y.push_back(ny);
    };
    
    rep(i, n) {
        int nx, ny;
        cin >> nx >> ny;
        add(nx, ny);
    }
    
    dsu uf(n+q);
    rep(qi, q) {
        int type;
        cin >> type;
        if (type == 1) {
            int nx, ny;
            cin >> nx >> ny;
            add(nx, ny);
        }
        else if (type == 2) {
            while (pq.size()) {
                auto [a, b] = pq.top().second;
                if (!uf.same(a, b)) break;
                pq.pop();
            }
            if (!pq.size()) {
                puts("-1");
                continue;
            }
            int k = pq.top().first;
            cout << k << '\n';
            while (pq.size() and pq.top().first == k) {
                auto [a, b] = pq.top().second; pq.pop();
                uf.merge(a, b);
            }
        }
        else {
            int u, v;
            cin >> u >> v;
            --u; --v;
            if (uf.same(u, v)) puts("Yes");
            else puts("No");
        }
    }
    
    return 0;
}

G. Accumulation of Wealth

\(ans_k = \displaystyle\sum\limits_{i} \binom{n-1}{k-1} \cdot p^k(1-p)^{i-k} \cdot d_i\),其中 \(d_i = \displaystyle\prod\limits_{j=i+1}^{n-1} (1+\frac{1-p}{j+1})\)
然后将组合数拆开得到,
\(ans_k = \displaystyle\sum\limits_{i} \dfrac{(i-1)!}{(k-1)!(i-k)!} \cdot p^k(1-p)^{i-k} \cdot d_i\)
再将和 \(i\) 无关的项提出来,
\( ans_k = \frac{p^k}{(k-1)!}\displaystyle\sum\limits_{i} \dfrac{(i-1)!}{(i-k)!} \cdot (1-p)^{i-k} \cdot d_i \)

可以发现,这里的和式就是个卷积的形式,跑一遍 \(\operatorname{NTT}\) 即可

代码实现
#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 modinv {
  int n; vector<mint> d;
  modinv(): n(2), d({0,1}) {}
  mint operator()(int i) {
    while (n <= i) d.push_back(-d[mint::mod()%n]*(mint::mod()/n)), ++n;
    return d[i];
  }
  mint operator[](int i) const { return d[i];}
} invs;
struct modfact {
  int n; vector<mint> d;
  modfact(): n(2), d({1,1}) {}
  mint operator()(int i) {
    while (n <= i) d.push_back(d.back()*n), ++n;
    return d[i];
  }
  mint operator[](int i) const { return d[i];}
} facts;
struct modfactinv {
  int n; vector<mint> d;
  modfactinv(): n(2), d({1,1}) {}
  mint operator()(int i) {
    while (n <= i) d.push_back(d.back()*invs(n)), ++n;
    return d[i];
  }
  mint operator[](int i) const { return d[i];}
} ifacts;
mint comb(int n, int k) {
  if (n < k || k < 0) return 0;
  return facts(n)*ifacts(k)*ifacts(n-k);
}

int main() {
    int n; mint p, q;
    {
        int _p;
        cin >> n >> _p;
        p = _p;
    }
    p /= 100; q = mint(1)-p;
    
    vector<mint> d(n);
    d[n-1] = 1;
    for (int i = n-2; i > 0; --i) {
        d[i] = d[i+1]*(q/(i+1) + 1);
    }
    
    vector<mint> fi(n), fj(n);
    for (int i = 1; i < n; ++i) fi[i] = d[i]*facts(i-1);
    rep(j, n) fj[j] = ifacts(n-1-j)*q.pow(n-1-j);
    auto fk = convolution(fi, fj);
    
    rep(k, n) {
        mint ans;
        if (k == 0) {
            ans = 1;
            for (int i = 1; i < n; ++i) ans += q*ans*invs(i);
        }
        else {
            ans = fk[k+(n-1)];
            ans *= ifacts(k-1)*p.pow(k);
        }
        cout << ans.val() << ' ';
    }
    
    return 0;
}