A. Count Takahashi

模拟

代码实现
n = int(input())
ans = 0
for i in range(n):
    s = input()
    if s == 'Takahashi':
        ans += 1
print(ans)

B. Couples

统计有多少个数对 \((i, i+2)\) 满足 \(a_i = a_{i+2}\) 即可

代码实现
n = int(input())
a = list(map(int, input().split()))
print(sum(a[i] == a[i+2] for i in range(n*2-2)))

C. Tile Distance 2

注意到,上下移动一格,不会产生费用的左右边界就会各增加 \(1\)。因此,如果在调整纵坐标的时候,如果目标在这个范围之外,那么就向横向移动即可。

代码实现
sx, sy = map(int, input().split())
tx, ty = map(int, input().split())

if (sx+sy)%2 == 1:
    sx -= 1
if (tx+ty)%2 == 1:
    tx -= 1
    
x = abs(tx-sx)
y = abs(ty-sy)
ans = 0
if x < y:
    ans = y
else:
    ans = (x+y)//2
print(ans)

D. Avoid K Palindrome

在满足条件的字符串末尾添加一个字符是否仍然满足条件,只需知道最后的 \(K-1\) 个字符即可

dp[i][S] 表示考虑到第 \(i\) 个字符且末尾的 \(K-1\) 个字符构成的字符串为 \(s\) 的合法方案数

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

bool isPalindrome(string s) {
    string ns = s;
    reverse(ns.begin(), ns.end());
    return s == ns;
}

int main() {
    int n, k;
    string s;
    cin >> n >> k >> s;
    
    map<string, mint> dp;
    dp[""] = 1;
    rep(i, n) {
        map<string, mint> old;
        swap(dp, old);
        for (auto [t, num] : old) {
            for (char c = 'A'; c <= 'B'; ++c) {
                if (s[i] != '?' and s[i] != c) continue;
                string nt = t+c;
                if (nt.size() == k and isPalindrome(nt)) continue;
                if (nt.size() == k) nt.erase(nt.begin());
                dp[nt] += num;
            }
        }
    }
    
    mint ans;
    for (auto [t, num] : dp) ans += num;
    cout << ans.val() << '\n';
    
    return 0;
}

E. Water Tank

这题本质上是单调栈求最大矩形的问题

首先可以表示出,第 \(i\) 格第一次有水是在:\(1 + \sum\limits_{j = 1}^i \max(H_j, H_{j+1}, \cdots, H_i)\)

考虑用单调栈来维护这个答案,单调栈维护一下当前后缀的最大值以及对应的区间长度即可。答案就是单调栈中所有元素的长度 \(\times\) 高度之和 \(+1\)
时间复杂度为 \(\mathcal{O}(N)\)

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

using namespace std;
using ll = long long;
using P = pair<ll, ll>;

int main() {
    int n;
    cin >> n;
    
    vector<int> h(n);
    rep(i, n) cin >> h[i];
    
    stack<P> st;
    ll now = 0;
    vector<ll> ans(n);
    rep(i, n) {
        ll w = 1;
        while (st.size() and st.top().first <= h[i]) {
            auto [nh, nw] = st.top(); st.pop();
            now -= nh*nw;
            w += nw;
        }
        st.emplace(h[i], w);
        now += h[i]*w;
        ans[i] = now+1;
    }
    
    rep(i, n) cout << ans[i] << ' ';
    
    return 0;
}

F. Tree Degree Optimization

注意到如果要构成树,对于点 \(i = 1, 2, \cdots, N\),一定得满足以下性质:

  • \(d_i \geqslant 1\)
  • \(\sum d_i = 2N-2\)

那么我们不妨将所有点的度数初始化为 \(1\),然后贪心地用小根堆来分摊掉剩下的 \(N-2\) 的度数

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

using namespace std;
using ll = long long;
using P = pair<ll, int>;

int main() {
    int n;
    cin >> n;
    
    vector<ll> a(n);
    rep(i, n) cin >> a[i];
    
    ll ans = 0;
    rep(i, n) ans += a[i];
    
    vector<int> d(n, 1);
    priority_queue<P, vector<P>, greater<P>> q;
    rep(i, n) q.emplace(a[i]*3ll, i);
    rep(ti, n-2) {
        auto [dif, i] = q.top(); q.pop();
        ans += dif;
        d[i]++;
        q.emplace(a[i]*(d[i]*2+1), i);
    }
    
    cout << ans << '\n';
    
    return 0;
}

G. Sum of Tree Distance

本题有一堆做法:点分治、dsu on tree、虚树、根号分治

这里仅介绍点分治的做法:

把路径分为子树内部的和跨越重心的,子树内部的递归处理,跨越重心的就维护一下当前已经遍历到的所有子树的每种颜色到重心的距离和以及点的个数,然后直接计算贡献即可。时间复杂度为 \(\mathcal{O}(N\log N)\)

代码实现
#include <bits/stdc++.h>
#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() {
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    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> a(n);
    rep(i, n) cin >> a[i];
    
    ll ans = 0;
    // centroid decomposition
    vector<bool> used(n);
    vector<int> sz(n);
    auto cd = [&](auto& cd, int v) -> void {
        // find centroid
        auto getCentroid = [&]() {
            auto dfs = [&](auto& f, int v, int p=-1) -> int {
                sz[v] = 1;
                for (int u : to[v]) {
                    if (u == p or used[u]) continue;
                    sz[v] += f(f, u, v);
                }
                return sz[v];
            };
            int tot = dfs(dfs, v), c = -1;
            auto dfs2 = [&](auto& f, int v, int p=-1) -> void {
                bool ok = (tot-sz[v])*2 <= tot;
                for (int u : to[v]) {
                    if (u == p or used[u]) continue;
                    f(f, u, v);
                    if (sz[u]*2 > tot) ok = false;
                }
                if (ok) c = v;
            };
            dfs2(dfs2, v);
            return c;
        };
        int c = getCentroid();
        
        used[c] = true;
        unordered_map<int, int> cnt;
        unordered_map<int, ll> sum;
        cnt[a[c]] = 1; sum[a[c]] = 0;
        for (int u : to[c]) {
            if (used[u]) continue;
            vector<P> ps;
            auto dfs = [&](auto& f, int v, int p=-1, int dep=1) -> void {
                ps.emplace_back(a[v], dep);
                for (int u : to[v]) {
                    if (u == p or used[u]) continue;
                    f(f, u, v, dep+1);
                }
            };
            dfs(dfs, u);
            for (auto [col, dep] : ps) {
                ans += sum[col] + (ll)dep*cnt[col];
            }
            for (auto [col, dep] : ps) {
                cnt[col]++;
                sum[col] += dep;
            }
        }
        for (int u : to[c]) {
            if (used[u]) continue;
            cd(cd, u);
        }
    };
    cd(cd, 0);
    
    cout << ans << '\n';
    
    return 0;
}