C. Giant Domino

首先如果 \(S_N \leqslant 2S_1\),那么只需要取出首尾两张骨牌即可
下面考虑一般情况
考虑将序列 \(S\) 做升序排序,位于 \(1\) 号骨牌前和 \(N\) 号骨牌后的骨牌都不需要再考虑了
取出 \(1\) 号骨牌和 \(N\) 号骨牌之间的所有骨牌,并保留原来的骨牌之间的相对顺序,对于相邻两张骨牌一定得满足 \(S_{i+1} \leqslant 2S_i\),否则无解
然后用二分来找不超过上次选的数的 \(2\) 倍的数中的最大值的位置,时间复杂度为 \(\mathcal{O}(N\log N)\)

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

using namespace std;

int solve() {
    int n;
    cin >> n;
    
    vector<int> s(n);
    rep(i, n) cin >> s[i];
    
    if (s.back() <= s[0]*2) return 2;
    
    {
        vector<int> ns;
        for (int x : s) {
            if (x < s[0]) continue;
            if (x > s.back()) continue;
            ns.push_back(x);
        }
        s = ns;
        n = s.size();
        ranges::sort(s);
    }
    
    rep(i, n-1) {
        if (s[i]*2 < s[i+1]) return -1;
    }
    
    int last = 0, ans = 2;
    while (s[last]*2 < s.back()) {
        last = upper_bound(s.begin(), s.end(), s[last]*2)-s.begin()-1;
        ans++;
    }
    return ans;
}

int main() {
    int t;
    cin >> t;
    
    while (t--) cout << solve() << '\n';
    
    return 0;
}

D. Make 2-Regular Graph

枚举环排列 \(P\)
操作次数就是 $N+M-2 \times $ 保留下来的边数 \(C\)
其中 \(M-C\) 表示需要删掉的边数,\(N-C\) 表示需要添加的边数
考虑将下标 \(i\)\(P_i\) 连一条有向边,会得到一颗基环树森林,且每个连通分量都是一个环,我们需要判掉出现自环和二元环的排列
时间复杂度为 \(\mathcal{O}(N!N)\)

代码实现
#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 g(n, vector<int>(n));
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        g[a][b] = g[b][a] = 1;
    }
    
    int ans = 999;
    vector<int> p(n);
    rep(i, n) p[i] = i;
    do {
        bool ok = true;
        rep(i, n) {
            if (p[i] == i) ok = false;
            if (p[p[i]] == i) ok = false;
        }
        if (ok) {
            int now = 0;
            rep(i, n) now += g[i][p[i]];
            ans = min(ans, n+m-now*2);
        }
    } while (next_permutation(p.begin(), p.end()));
    
    cout << ans << '\n';
    
    return 0;
}

E. LCM Sequence

显然序列 \(A\) 一定单调不减
注意到 \(A_i \neq A_{i-1}\) 当且仅当 \(i\) 可以分解成素数的幂次
那么答案就是 \([L+1, R]\) 中可分解为素数幂次的数的个数 \(+1\)
跑一遍区间筛求出 \([L+1, R]\) 中每个数的素因子个数即可,这里只需要用到 \(1e7\) 以内的质数

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

using namespace std;
using ll = long long;

int main() {
    ll l, r;
    cin >> l >> r;
    ++l;
    
    ll w = r-l+1;
    vector<ll> x(w), cnt(w);
    rep(i, w) x[i] = l+i;
    
    const int M = 1e7;
    vector<bool> isp(M, true);
    isp[0] = isp[1] = false;
    for (int i = 2; i < M; ++i) {
        if (!isp[i]) continue;
        for (int j = i+i; j < M; j += i) isp[j] = false;
        
        for (ll j = (l+i-1)/i*i; j <= r; j += i) {
            while (x[j-l]%i == 0) x[j-l] /= i;
            cnt[j-l]++;
        }
    }
    
    int ans = 1;
    rep(i, w) {
        if (x[i] != 1) cnt[i]++;
        if (cnt[i] == 1) ans++;
    }
    
    cout << ans << '\n';
    
    return 0;
}

F. Socks 4

当两只袜子颜色不同时,保留数量较多的一只更优 \(\to\) 因此持有的袜子颜色会逐渐向数量多的颜色演化
dp[i] 表示从持有颜色 \(i\) 袜子开始时的期望值
转移方程:

\(S = \sum A_i\)

\( \begin{aligned} dp[i] &= \dfrac{1}{S}\left(\Big(\sum_{j < i} A_j\Big) dp[i] + (A_i-1) \times 0 + \Big(\sum_{j > i} A_j dp[j]\Big)\right) + 1\\ &= \dfrac{\sum\limits_{j < i} A_j}{S} dp[i] + \dfrac{\sum\limits_{j > i} A_j dp[j]}{S} + 1 \end{aligned} \)

\( \left(1 - \dfrac{\sum\limits_{j < i} A_j}{S}\right)dp[i] = \dfrac{\sum\limits_{j > i} A_j dp[j]}{S} + 1 \)

\( dp[i] = \dfrac{\dfrac{\sum\limits_{j > i} A_j dp[j]}{S} + 1}{1 - \dfrac{\sum\limits_{j < i} A_j}{S}} = \dfrac{\Big(\sum\limits_{j > i} A_j dp[j]\Big) + S}{S-\sum\limits_{j < i} A_j} = \dfrac{\Big(\sum\limits_{j > i} A_j dp[j]\Big) + S}{\Big(\sum\limits_{j \geqslant i} A_j\Big) - 1} \)

对于 \(\Big(\sum\limits_{j \geqslant i} A_j\Big) - 1\)\(\sum\limits_{j > i} A_j dp[j]\) 这两部分,可以用前缀和来加速

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

int main() {
    int n, c;
    cin >> n >> c; --c;
    
    vector<int> a(n);
    rep(i, n) cin >> a[i];
    int s = 0;
    rep(i, n) s += a[i];
    a[c]++;
    {
        int na = a[c];
        ranges::sort(a);
        rep(i, n) if (a[i] == na) c = i;
    }
    
    vector<mint> dp(n);
    mint suma = -1, sumx = 0;
    for (int i = n-1; i >= 0; --i) {
        suma += a[i];
        dp[i] = (sumx+s)/suma;
        sumx += dp[i]*a[i];
    }
    
    cout << dp[c].val() << '\n';
    
    return 0;
}

G. Degree Harmony

带花树