Codeforces Round #722 (Div. 2)

比赛链接:https://codeforces.com/contest/1529

A. Eshag Loves Big Arrays

题解

反复选取最小值和大于它的数即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        cout << n - count(a.begin(), a.end(), *min_element(a.begin(), a.end())) << "
";
    }
    return 0;
}

B. Sifid and Strange Subsequences

题解

因为要求任意两对都大于等于最大值,所以排序找相邻最小值,然后枚举当前值能否为最大值即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        sort(a.begin(), a.end());
        int mi = INT_MAX, ans = 0;
        for (int i = 0; i < n; i++) {
            if (i > 0) {
                mi = min(mi, a[i] - a[i - 1]);
            }
            if (mi >= a[i]) {
                ans = i + 1;
            }
        }
        cout << ans << "
";
    }
    return 0;
}

C. Parsa's Humongous Tree

题解

每个结点取 (l_i)(r_i) 收益最大,树形 (dp)

证明

假设一个结点取 ((l_i, r_i)) 间的某值,设与它相邻的结点中小于此值的有 (p) 个,大于此值的有 (q) 个,根据 (p, q) 的大小关系可以分为以下三种情况:

  • (p < q) ,此时取值向 (l_i) 靠近收益一定增加
  • (p > q) ,此时取值向 (r_i) 靠近收益一定增加
  • (p = q) ,此时取值向 (l_i)(r_i) 靠近收益一定不会减少

所以,每个结点只取 (l_i)(r_i) 即可。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> l(n), r(n);
        for (int i = 0; i < n; i++) {
            cin >> l[i] >> r[i];
        }
        vector<vector<int>> G(n);
        for (int i = 0; i < n - 1; i++) {
            int u, v;
            cin >> u >> v;
            --u, --v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        vector<vector<long long>> dp(n, vector<long long> (2));
        function<void(int, int)> dfs = [&](int u, int p) {
            for (auto v : G[u]) {
                if (v == p) {
                    continue;
                }
                dfs(v, u);
                dp[u][0] += max(dp[v][0] + abs(l[u] - l[v]), dp[v][1] + abs(l[u] - r[v]));
                dp[u][1] += max(dp[v][0] + abs(r[u] - l[v]), dp[v][1] + abs(r[u] - r[v]));
            }
        };
        dfs(0, -1);
        cout << max(dp[0][0], dp[0][1]) << "
";
    }
    return 0;
}

D. Kavi on Pairing Duty

题解

假设点 (1)(x) 配对,根据 (x)(n) 的大小关系可以分为以下两种情况:

(x > n)

  • 此时 (x + 1) 由于不被 ([1,x]) 包含,只能与 (2) 配对。

  • 同理, ([x, 2n]) 间的点只能依次与 ([1, 1 + 2n - x]) 配对。

  • 此时在 ((1 + 2n - x, x)) 中还有 (2(x - n - 1)) 个点,即余下的点总为 (2) 的倍数,所以可以设 (dp_i)(2i) 个点时的方案数。

  • (x) 取大于 (n)(n+1, n+2,dots,2n) 时,余下的点依次为 (2 imes (0,1,dots,n-1)) 个,由于这些点被所有区间包含,所以只需考虑它们自身即可。即,当 (x > n) 时,总的方案数之和为 (sum limits _{i = 0}^{n - 1} dp_i)

(x le n)

  • 此时 (x + 1) 可以与 (2)(2x) 配对。若 (x + 1)(2x) 配对,由于 (2) 不被 ([x + 1, 2x]) 包含所以必须与 (x + 1) 配对,矛盾,所以 (x + 1) 仍只能与 (2) 配对。

  • 同理, ([x, 2(x - 1)]) 间的点只能依次与 ([1, x - 1]) 配对。

  • 此时 (2n) 个点被分割成了一个个长为 (2(x - 1)) 的小整体,所以 (x - 1) 需要整除 (n) ,又由于 (x - 1 le n - 1) ,所以共有 (n) 的真因子个数种分法。即 (x le n) 的情况共有 (div_n) 种分法。

综上,推得 (dp_n = div_n + sum limits _{i = 0}^{n - 1} dp_i)

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    constexpr int N = 1e6 + 10;
    vector<int> div(N);
    for (int i = 1; i < N; i++) {
        for (int j = i + i; j < N; j += i) {
            ++div[j];
        }
    }
    constexpr int MOD = 998244353;
    vector<int> dp(N);
    int sum = dp[0] = 1;
    for (int i = 1; i < N; i++) {
        dp[i] = (div[i] + sum) % MOD;
        sum = (sum + dp[i]) % MOD;
    }
    int n;
    cin >> n;
    cout << dp[n] << "
";
    return 0;
}

E. Trees of Tranquillity

题解

由于构造图为完全图,所以其中的点在树一中两两存在父子关系,即在树一中为一条链,所以可以用 (dfs) 回溯遍历树一中的链。

为了快速判断两点在树二上是否存在父子关系,可以用 (dfs) 序打时间戳,若 (v)(u) 的子代,则一定有 (in_u < in_v < out_u) ,同时,由于贪心策略,若两点在树二中存在父子关系,取子结点更优。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<vector<int>> G1(n);
        for (int v = 1; v < n; v++) {
            int u;
            cin >> u;
            --u;
            G1[u].push_back(v);
        }
        vector<vector<int>> G2(n);
        for (int v = 1; v < n; v++) {
            int u;
            cin >> u;
            --u;
            G2[u].push_back(v);
        }
        vector<int> in(n), out(n), id(n);
        int timeStamp = 0;
        function<void(int)> dfsG2 = [&](int u) {
            in[u] = timeStamp++;
            id[in[u]] = u;
            for (auto v : G2[u]) {
                dfsG2(v);
            }
            out[u] = timeStamp;
        };
        dfsG2(0);
        int ans = 0;
        set<int> st;
        function<void(int)> dfsG1 = [&](int u) {
            bool add = false;
            int del = -1;
            auto it = st.upper_bound(in[u]);
            if (it != st.end() and *it < out[u]) { //若 set 中已有子结点,由贪心策略,取子结点更优
                
            } else {
                if (it != st.begin()) {
                    --it;
                    if (out[id[*it]] > in[u]) { //同上, erase 掉父结点
                        del = *it;
                        st.erase(it);
                    }
                }
                st.insert(in[u]);
                add = true;
            }
            ans = max(ans, (int)st.size());
            for (auto v : G1[u]) {
                dfsG1(v);
            }
            if (add) {
                st.erase(in[u]);
                if (del != -1) {
                    st.insert(del);
                }
            }
        };
        dfsG1(0);
        cout << ans << "
";
    }
    return 0;
}

F. It's a bird! No, it's a plane! No, it's AaParsa!

题解

本质上还是最短路问题,只不过多出了每个点等待的时间。

由于点的个数 (n) 远少于边数 (m) ,所以采用 (O_{(n^2)})(Dijkstra)

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, int>>> G(n);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        G[u].emplace_back(v, w);
    }
    const int INF = 1e9 + n;
    for (int s = 0; s < n; s++) {
        vector<int> dis(n, INF);
        vector<bool> vis(n);
        for (auto [v, w] : G[s]) {
            dis[v] = w;
        }
        for (int loop = 0; loop < n; loop++) {
            int u = -1;
            for (int i = 0; i < n; i++) {
                if (vis[i]) {
                    continue;
                }
                if (u == -1 or dis[i] < dis[u]) {
                    u = i;
                }
            }
            vis[u] = true;
            for (int wait = 0; wait < n; wait++) {
                dis[(u + wait) % n] = min(dis[(u + wait) % n], dis[u] + wait);
            } //其实只考虑每个点等 1 秒的情况即可,和等 n 秒的情况是等价的
            for (auto [v, w] : G[u]) {
                dis[(v + dis[u]) % n] = min(dis[(v + dis[u]) % n], w + dis[u]);
            }
        }
        dis[s] = 0;
        for (int i = 0; i < n; i++) {
            cout << dis[i] << " 
"[i == n - 1];
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Kanoon/p/14842534.html