T1:Echo

模拟

代码实现
n = int(input())
s = input()

ans = ''
for c in s:
    ans += c+c
print(ans)

T2:Base 2

C++ 中 long long 最大为 \(2^{63}-1\)unsigned long long 最大为 \(2^{64}-1\)

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

using namespace std;
using ull = unsigned long long;

int main() {
    ull ans = 0;
    rep(i, 64) {
        ull a;
        cin >> a;
        ans += a<<i;
    }
    
    cout << ans << '\n';
    
    return 0;
}

T3:Centers

可以开一个桶来记录每个数的出现次数

在扫描每个数的同时,更新 \(a_i\) 的出现次数,如果此时这个值为 \(2\),则将 \(a_i\) 添加到答案末尾

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

using namespace std;

int main() {
    int n;
    cin >> n;
    
    int n3 = n*3;
    vector<int> a(n3);
    rep(i, n3) cin >> a[i];
    
    vector<vector<int>> ps(n+1);
    rep(i, n3) ps[a[i]].push_back(i);
    
    vector<int> cnt(n+1);
    vector<int> ans;
    rep(i, n3) {
        cnt[a[i]]++;
        if (cnt[a[i]] == 2) ans.push_back(a[i]);
    }
    
    rep(i, n) cout << ans[i] << ' ';
    
    return 0;
}

T4:Poisonous Full-Course

必要的信息:

  • 当前是否中毒

dp[i][p] 表示从前 \(i\) 道菜中选择若干道菜使得当前的健康状态为 \(p\) 时的美味度总和的最大值

最后的答案为 \(\max(dp[n][0], dp[n][1])\)

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

using namespace std;
using ll = long long;

inline void chmax(ll& x, ll y) { if (x < y) x = y; }

int main() {
    int n;
    cin >> n;
    
    vector<int> x(n), y(n);
    rep(i, n) cin >> x[i] >> y[i];
    
    const ll INF = 1e18;
    vector dp(n+1, vector<ll>(2, -INF));
    dp[0][0] = 0;
    rep(i, n) {
        chmax(dp[i+1][0], dp[i][0]);
        chmax(dp[i+1][1], dp[i][1]);
        
        if (x[i] == 0) {
            chmax(dp[i+1][0], dp[i][0]+y[i]);
            chmax(dp[i+1][0], dp[i][1]+y[i]);
        }
        else {
            chmax(dp[i+1][1], dp[i][0]+y[i]);
        }
    }
    
    ll ans = max(dp[n][0], dp[n][1]);
    cout << ans << '\n';
    
    return 0;
}

T5:Best Performances

需要设计一个数据结构满足以下需求:

  • 删除
  • 添加
  • 查询最大的 \(k\) 个数的和

可以用两个 std::multiset 来实现,一个用来维护前 \(k\) 大的数,另一个用来维护剩下的数

代码实现
#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, k, q;
	cin >> n >> k >> q;
	
	vector<int> a(n);
	multiset<int> s, t;
	rep(i, k) s.insert(0);
	rep(i, n-k) t.insert(0);
	ll ans = 0;
	
	auto add = [&](int x) {
	    s.insert(x); ans += x;
	    int y = *s.begin();
	    s.erase(s.find(y)); ans -= y;
	    t.insert(y);
	};
	auto del = [&](int x) {
	    if (s.find(x) != s.end()) {
	        s.erase(s.find(x)); ans -= x;
	        int y = *t.rbegin();
	        t.erase(t.find(y));
	        s.insert(y); ans += y;
	    } 
	    else {
	        t.erase(t.find(x)); 
	    }
	};
	
	rep(qi, q) {
	    int x, y;
	    cin >> x >> y;
	    --x; 
	    add(y);
	    del(a[x]);
	    a[x] = y;
	    cout << ans << '\n';
	}
	
	return 0;
}

T6:Merge Sets

对于 \(f(S_1, S_2)\) 而言,假设 \(x \in S_1\),那么 \(x\)\(f\) 的贡献就是他在 \(S_1\) 中按升序排序后的排名,再加上 \(S_2\) 中小于 \(x\) 的数的个数
由于前面一部分的总和为 \(1+2+\cdots+m = \frac{m(m+1)}{2}\) 是固定的,所以可以先对 \(S_1\) 做升序排序,然后用树状数组来统计第二部分的贡献
由于一共要计算 \(\binom{n}{2}\) 项的总和,所以第一部分总的贡献就是 \(\frac{m(m+1)}{2} \times \binom{n}{2}\)
第二部分总的贡献就是将每个集合排序后拼接起来的序列的逆序数

时间复杂度为 \(\mathcal{O}(nm\log(nm))\)

代码实现
#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 ll = long long;
using P = pair<int, int>;

int main() {
	int n, m;
	cin >> n >> m;
	
	vector a(n, vector<int>(m));
	rep(i, n)rep(j, m) cin >> a[i][j];
	
	vector<P> b;
	rep(i, n)rep(j, m) b.emplace_back(a[i][j], i);
	sort(b.begin(), b.end());
	
	ll ans = ll((m+1)*m/2) * (n*(n-1)/2);
	fenwick_tree<int> t(n);
	for (auto [_, x] : b) {
	    t.add(x, 1);
	    ans += t.sum(x+1, n);
	}
	
	cout << ans << '\n';
	
	return 0;
}

T7:Return to 1

首先将和点 \(1\) 不在同一个强连通分量中的点删掉

\(S\) 为所有包含点 \(1\) 的环的长度的集合,\(g\)\(S\) 中所有数的最大公约数
当且仅当 \(g\)\(10^{10^{100}}\) 的因子时,答案是有解的。

\(S\) 可能是无限集,这样就找不到 \(g\) 了。

现在,任取一个以点 \(1\) 为根的 \(\operatorname{dfs}\) 生成树 \(T\),令 \(d_i\) 为点 \(i\)\(T\) 上的深度

性质:

  • 对于任意正整数 \(x\) 而言,\(S\) 中包含一个不是 \(x\) 的倍数的数 \(\Leftrightarrow\) 存在一条有向边 \(u \to v\),使得 \(|d_u+1-d_v| \neq 0 \pmod x\)

对于可行解的 \(g\) 中只存在素因子 \(p = 2, 5\)
考虑环长是否是 \(p\) 的倍数

环的长度为 \(p\) 的倍数
\(\Leftrightarrow\) 对于所有的边 \(u \to v\),有 \(d[u]+1 \equiv d[v] \pmod p\)
\(\Leftrightarrow\) 对于所有的边 \(u \to v\),有 \(d[u]+1 - d[v] \equiv 0 \pmod p\)
\(\Leftrightarrow\) 对于所有的边 \(u \to v\),有 \(p\) 整除 \(d[u]+1-d[v]\)
\(\Leftrightarrow\) \(p\) 整除所有的边 \(u \to v\) 对应的 \(d[u]+1-d[v]\)\(\gcd\)

所以,令 \(g'\)\(T\) 上所有非树边对应的 \(|d_u+1-d_v|\) 的最大公约数,此时 \(g' = g\)

加强版:

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

using namespace std;

void solve() {
    int n, m;
    cin >> n >> m;
    
    vector<vector<int>> to(n), ot(n);
    rep(i, m) {
        int a, b;
        cin >> a >> b;
        --a; --b;
        to[a].push_back(b);
        ot[b].push_back(a);
    }
    
    vector<bool> ok(n);
    {
        queue<int> q;
        q.push(0); ok[0] = true;
        while (q.size()) {
            int v = q.front(); q.pop();
            for (int u : ot[v]) {
                if (ok[u]) continue;
                ok[u] = true;
                q.push(u);
            }
        }
    }
    
    int g = 0;
    vector<int> d(n, -1);
    auto dfs = [&](auto f, int v, int nd=0) -> void {
        if (d[v] != -1) {
            int x = abs(nd-d[v]);
            if (x and ok[v]) g = gcd(g, x);
            return;
        }
        d[v] = nd;
        for (int u : to[v]) {
            f(f, u, nd+1);
        }
    };
    dfs(dfs, 0);
    
    while (g and g%2 == 0) g /= 2;
    while (g and g%5 == 0) g /= 5;
    if (g == 1) puts("Yes");
    else puts("No");
}

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