T1:3.14

模拟

代码实现
s = '3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679'
n = int(input())
ans = s[0:n+2]
print(ans)

T2:Roulette

模拟

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

using namespace std;

int main() {
    int n;
    cin >> n;
    
    vector<int> c(n);
    vector<vector<int>> a(n);
    rep(i, n) {
        cin >> c[i];
        a[i] = vector<int>(c[i]);
        rep(j, c[i]) cin >> a[i][j];
    }
    
    int x;
    cin >> x;
    
    vector<bool> bet(n);
    rep(i, n) {
        bet[i] = any_of(a[i].begin(), a[i].end(), [&](int e) { return e == x;});
    }
    
    int cmin = 37;
    rep(i, n) {
        if (bet[i]) cmin = min(cmin, c[i]);
    }
    
    vector<int> ans;
    rep(i, n) {
        if (bet[i] and c[i] == cmin) ans.push_back(i+1);
    }
    
    cout << ans.size() << '\n';
    for (int i : ans) cout << i << ' ';
    
    return 0;
}

T3:Rotate Colored Subsequence

模拟
可以预处理一下每种颜色的位置

代码实现
#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;
    cin >> s;
    vector<int> c(n);
    rep(i, n) cin >> c[i];
    
    vector<vector<int>> ps(m);
    rep(i, n) ps[c[i]-1].push_back(i);
    
    string ans = s;
    rep(i, m) {
        int l = ps[i].size();
        rep(j, l) {
            ans[ps[i][(j+1)%l]] = s[ps[i][j]];
        }
    }
    
    cout << ans << '\n';
    
    return 0;
}

T4:LOWER

显然只有最后一个大小写颠倒的操作有效,那么我们只需找到这个时刻,按顺序在此时刻之前的操作 \(1\),然后在此时刻做大小写颠倒操作,在此操作之后继续执行操作 \(1\)

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

using namespace std;

int main() {
    int n, q;
    string s;
    cin >> n >> s >> q;
    
    vector<int> lt(n);
    int last = -1, type = 2;
    rep(qi, q) {
        int t, x; char c;
        cin >> t >> x >> c;
        --x;
        if (t == 1) {
            s[x] = c;
            lt[x] = qi;
        }
        else {
            last = qi;
            type = t;
        }
    }
    
    rep(i, n) {
        if (lt[i] < last) {
            if (type == 2) s[i] = tolower(s[i]);
            else s[i] = toupper(s[i]);
        }
    }
    
    cout << s << '\n';
    
    return 0;
}

T5:Roulettes

期望dp

dp[x] 表示剩余 \(x\) 点的状态的期望

初始值:\(\operatorname{dp}[0] = 0\)

答案为 \(\operatorname{dp}[M]\)

注意需要特判 \(S_{ij} = 0\) 时的情况

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

using namespace std;

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

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> c(n), p(n);
    vector<vector<int>> a(n);
    rep(i, n) {
        cin >> c[i] >> p[i];
        a[i] = vector<int>(p[i]);
        rep(j, p[i]) cin >> a[i][j];
    }
    
    const double INF = 1e18;
    vector<double> dp(m+1, INF);
    dp[0] = 0;
    for (int r = 1; r <= m; ++r) {
        rep(i, n) {
            double now = 0;
            double b = 0;
            for (int d : a[i]) {
                if (d) now += dp[max(0, r-d)];
                else b += 1;
            }
            now /= p[i]; b /= p[i];
            now += c[i]; now /= 1-b;
            chmin(dp[r], now);
        }
    }
    
    printf("%.10f\n", dp[m]);
    
    return 0;
}

T6:A Certain Game

在合并过程树上自顶向下做树形dp即可,可以用并查集来做合并

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

using namespace std;
using mint = modint998244353;

int main() {
    int n;
    cin >> n;
    
    vector<vector<int>> to(n);
    vector<int> sz(n*2-1, 1);
    vector<int> gv(n);
    rep(i, n) gv[i] = i;
    dsu uf(n);
    rep(i, n-1) {
        int p, q;
        cin >> p >> q;
        --p; --q;
        p = uf.leader(p);
        q = uf.leader(q);
        
        int v = to.size();
        to.push_back({gv[p], gv[q]});
        uf.merge(p, q);
        gv[uf.leader(p)] = v;
        sz[v] = uf.size(p);
    }
    
    int root = to.size()-1;
    vector<mint> dp(to.size());
    auto dfs = [&](auto f, int v) -> void {
        for (int u : to[v]) {
            dp[u] = dp[v] + mint(sz[u])/sz[v];
            f(f, u);
        }
    };
    dfs(dfs, root);
    
    rep(i, n) cout << dp[i].val() << ' ';
    
    return 0;
}

T7:Amulets

我们不去考虑有 \(i\) 个护身符时能打倒几只怪兽,而是考虑为了打倒 \(i\) 只怪兽需要几个护身符。
这样一来,原问题就变成了处理对序列进行 “插入一个数” “删除一个数”以及 “求至少选多少个数才能使总和 \(\geqslant X\)” 的询问的问题!
这个问题可以用一个 std::multiset 维护所有 \(\geqslant X\) 的数,以及另一个 std::multiset 维护剩下的数来解决!

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

using namespace std;
using ll = long long;

struct S {
    ll h;
    multiset<ll> s, t;
    S(int h): h(h) {}
    void fix() {
        while (h <= 0) {
            h += *s.rbegin();
            t.insert(*s.rbegin());
            s.erase(prev(s.end()));
        }
        while (t.size() and h > *t.begin()) {
            h -= *t.begin();
            s.insert(*t.begin());
            t.erase(t.begin());
        }
    }
    void add(ll x) {
        if (t.size() and *t.begin() < x) t.insert(x);
        else h -= x, s.insert(x);
        fix();
    }
    void del(ll x) {
        if (t.find(x) != t.end()) {
            t.erase(t.find(x));
        }
        else {
            h += x;
            s.erase(s.find(x));
            fix();
        }
    }
    int get() { return t.size(); }
};

int main() {
	int n, m, h;
	cin >> n >> m >> h;
	
	vector<int> a(n), b(n);
	rep(i, n) cin >> a[i] >> b[i], b[i]--;
	
	vector<ll> sum(m);
	S s(h);
	rep(i, m) s.add(0);
	vector<int> ans(m+1);
	rep(i, n) {
	    s.del(sum[b[i]]);
	    sum[b[i]] += a[i];
	    s.add(sum[b[i]]);
	    ans[s.get()] = i+1;
	}
	rep(i, m) ans[i+1] = max(ans[i+1], ans[i]);
	
	rep(i, m+1) cout << ans[i] << ' ';
	
	return 0;
}