C. Cycle Graph?

如果图满足连通且所有点的度数为 \(2\),那么就是环图!

代码实现
#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<int> deg(n);
    dsu uf(n);
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        deg[a]++;
        deg[b]++;
        uf.merge(a, b);
    }
    
    if (deg == vector<int>(n, 2) and uf.size(0) == n) {
        puts("Yes");
    }
    else puts("No");
    
    return 0;
}

D. Goin' to the Zoo

对每个动物园做三进制枚举

代码实现
#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, m;
    cin >> n >> m;
    
    vector<int> c(n);
    rep(i, n) cin >> c[i];
    
    vector<vector<int>> a(m);
    rep(i, m) {
        int k;
        cin >> k;
        a[i] = vector<int>(k);
        rep(j, k) cin >> a[i][j], a[i][j]--;
    }
    
    vector<int> p3(n+1, 1);
    rep(i, n) p3[i+1] = p3[i]*3;
    
    const ll INF = 1e18;
    ll ans = INF;
    rep(s, p3[n]) {
        vector<int> t(n);
        rep(i, n) t[i] = s/p3[i]%3;
        ll cost = 0;
        rep(i, n) cost += c[i]*t[i];
        rep(j, m) {
            int cnt = 0;
            for (int i : a[j]) cnt += t[i];
            if (cnt < 2) cost = INF;
        }
        ans = min(ans, cost);
    }
    
    cout << ans << '\n';
    
    return 0;
}

E. Bowls and Beans

不需要将一碗豆子分散移动,也不需要越过其他豆子。也就是说,从右边的豆子开始,依次“合并到左边相邻的豆子所在的碗中”是最优的!剩下的就是最短路问题了!

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

using namespace std;

int solve(vector<int> c) {
    int n = c.size();
    const int INF = 1001001001;
    vector<int> dp(n+1, INF);
    dp[0] = 0;
    rep(i, n) {
        int now = INF;
        rep(j, c[i]) {
            if (0 <= i-j) now = min(now, dp[i-j]);
        }
        dp[i+1] = now+1;
    }
    return dp[n];
}

int main() {
    int n;
    cin >> n;
    
    vector<int> c(n), a(n);
    rep(i, n-1) cin >> c[i+1];
    rep(i, n-1) cin >> a[i+1];
    a[0] = 1;
    
    int ans = 0;
    vector<int> nc;
    for (int i = 1; i < n; ++i) {
        nc.push_back(c[i]);
        if (a[i]) {
            ans += solve(nc);
            nc = vector<int>();
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

这里的 \(N\) 的范围比较小,即使是暴力dp跑的也很快
如果 \(N\) 再开大点,可以用线段树来优化

代码实现
const int INF = 1001001001;
int op(int a, int b) { return min(a, b); }
int e() { return INF; }

int solve(vector<int> c) {
    int n = c.size();
    segtree<int, op, e> dp(n+1);
    dp.set(0, 0);
    rep(i, n) {
        int l = max(0, i-c[i]+1), r = i+1;
        int now = dp.prod(l, r);
        dp.set(i+1, now+1);
    }
    return dp.get(n);
}

F. Lost and Pound

概率dp
dp[i][j] 表示到第 \(i\) 次操作为止已经按下 \(j\) 次获胜按钮的状态继续游戏时的胜率
然后从后往前进行转移即可
对于转移,还需要开一个辅助dp
dp2[i][j] 表示到 \(c_i\) 为止总共按了 \(j\) 次按钮时的胜率的总和的最大值

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

using namespace std;

inline void chmax(double& a, double b) { if (a < b) a = b; }

int main() {
    int n, t, m, k;
    cin >> n >> t >> m >> k;
    
    vector dp(t+1, vector<double>(k+1));
    dp[t][k] = 1;
    for (int ti = t-1; ti >= 0; --ti) {
        rep(kj, k+1) {
            double now = 0;
            {
                vector dp2(m+1, vector<double>(m+1));
                rep(i, m)rep(j, m+1) {
                    for (int c = 1; j+c <= m; ++c) {
                        chmax(dp2[i+1][j+c], dp2[i][j]+dp[ti+1][min(k, kj+c)]);
                    }
                }
                rep(i, m+1) {
                    if (i > n) break;
                    double x = dp2[i][m];
                    x += dp[ti+1][kj]*(n-i);
                    chmax(now, x);
                }
            }
            dp[ti][kj] = now/n;
        }
    }
    
    double ans = dp[0][0];
    printf("%.10f\n", ans);
    
    return 0;
}

G. Specified Range Sums

差分约束板题
\(C_i = \sum\limits_{k=1}^i A_k\)
那么 \(S = A_l + \cdots + A_r = C_r - C_{l-1}\)
\(\Rightarrow\) \(C_r - C_{l-1} \geqslant S\)\(C_r - C_{l-1} \leqslant S\)

\(A_i\) 是正整数 \(\Rightarrow\) \(C_i - C_{i-1} \geqslant 1\)

代码实现
#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, m;
    cin >> n >> m;
    
    vector<tuple<int, int, int>> edges;
    rep(i, n) edges.emplace_back(i+1, i, -1);
    rep(i, m) {
        int l, r, s;
        cin >> l >> r >> s;
        --l;
        edges.emplace_back(r, l, -s);
        edges.emplace_back(l, r, s);
    }
    
    const ll INF = 1e18;
    vector<ll> dist(n+1, INF);
    dist[n] = 0;
    
    bool upd = true;
    rep(ti, n+1) {
        upd = false;
        for (auto [a, b, c] : edges) {
            ll nd = dist[a]+c;
            if (dist[b] > nd) {
                upd = true;
                dist[b] = nd;
            }
        }
    }
    
    if (upd) puts("-1");
    else cout << -dist[0] << '\n';
    
    return 0;
}