Educational Codeforces Round 110 (Rated for Div. 2)【A A. Fair Playoff B. Array Reodering C. Unstable String D. Playoff Tournament E. Gold Transfer

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

题解

可以考虑不成立的情况。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        if (min(a, b) > max(c, d) or min(c, d) > max(a, b)) {
            cout << "NO" << "
";
        } else {
            cout << "YES" << "
";
        }
    }
    return 0;
}

B. Array Reodering

题解

为了尽可能地与 (2a_j) 配对,将因子中含有 (2)(a_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> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        sort(a.begin(), a.end(), [](int x, int y) {
            if (x % 2 != y % 2) {
                return x % 2 == 0;
            } else {
                return x < y;
            }
        });
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (gcd(a[i], 2 * a[j]) > 1) {
                    ++ans;
                }
            }
        }
        cout << ans << "
";
    }
    return 0;
}

C. Unstable String

题解

(dp_{i 0/1}) 表示第 (i) 位为 (0/1) 时,以第 (i) 位为终点的 (01) 间隔串的最大长度。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        string s;
        cin >> s;
        long long ans = 0;
        int n = s.size();
        vector<vector<int>> dp(n, vector<int> (2));
        for (int i = 0; i < n; i++) {
            if (i == 0) {
                if (s[i] == '?') {
                    dp[i][0] = dp[i][1] = 1;
                } else if (s[i] == '0') {
                    dp[i][0] = 1;
                } else {
                    dp[i][1] = 1;
                }
            } else {
                if (s[i] == '?') {
                    dp[i][0] = dp[i - 1][1] + 1;
                    dp[i][1] = dp[i - 1][0] + 1;
                } else if (s[i] == '0') {
                    dp[i][0] = dp[i - 1][1] + 1;
                } else {
                    dp[i][1] = dp[i - 1][0] + 1;
                }
            }
            ans += max(dp[i][0], dp[i][1]);
        }
        cout << ans << "
";
    }
    return 0;
}

D. Playoff Tournament

题解

由于整个比赛状态是一棵满二叉树,在满二叉树中,根节点序号为 (1) ,序号为 (i) 的结点左子结点序号为 (2i) ,右子结点序号为 (2i + 1) ,父结点为 (lfloor frac{i}{2} floor) ,所以可以用数组来实现。

接下来将叶子结点视为队伍,父结点视为比赛,按照如下方案更新每个结点:

  • 初始时所有叶子结点的方案数赋为 (1) ,代表每支队伍

  • 若某结点对应的比赛结果为 (0) ,则只继承左子结点的方案数

  • 若某结点对应的比赛结果为 (1) ,则只继承右子结点的方案数

  • 若某结点对应的比赛结果为 (?) ,则继承左子结点与右子结点的方案数之和

同理,在每次询问中,更新某次比赛结果后从其对应的结点往上修改即可。

Tips

为了方便在二叉树结点与比赛序号间进行转换,可以将结点 ([2^k-1,1]) 与比赛 ([0,2^k-2]) 依次对应,这样二者始终满足其和为 (2^k - 1) ,同时,为了满足左右子结点大小关系恒定的性质,队伍依次在底端从右往左编号,也因此需要交换比赛结果为 (0)(1) 时的处理方案。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int k;
    cin >> k;
    string s;
    cin >> s;
    const int n = 1 << k;
    vector<int> dp(2 * n);
    for (int i = n; i < 2 * n; i++) {
        dp[i] = 1;
    }
    for (int i = n - 1; i >= 1; i--) {
        dp[i] = s[n - 1 - i] == '0' ? dp[2 * i + 1] : (s[n - 1 - i] == '1' ? dp[2 * i] : dp[2 * i + 1] + dp[2 * i]);
    }
    int q;
    cin >> q;
    while (q--) {
        int p;
        char c;
        cin >> p >> c;
        --p;
        s[p] = c;
        for (int i = n - 1 - p; i != 0; i /= 2) {
            dp[i] = s[n - 1 - i] == '0' ? dp[2 * i + 1] : (s[n - 1 - i] == '1' ? dp[2 * i] : dp[2 * i + 1] + dp[2 * i]);
        }
        cout << dp[1] << "
";
    }
    return 0;
}

E. Gold Transfer

题解

由于父结点的花费小于子结点,所以始终选择路径上距离根结点最近且不为空的结点。

由于路径较长,考虑使用倍增,设 (p_{ij}) 是与结点 (i) 距离为 (2^j) 的结点,那么有状态转移方程: (p_{i j+1} = p_{p_{ij} j}) ,含义为 (2^{j + 1} = 2^j + 2^j)

之后依次贪心查找即可,每次查找的过程可以看作与最远非空结点间距离的二进制表示。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    constexpr int N = 20;
    int q;
    cin >> q;
    vector<int> a(q + 1), c(q + 1), dep(q + 1);
    vector<vector<int>> p(q + 1, vector<int> (N, -1));
    cin >> a[0] >> c[0];
    for (int i = 1; i <= q; i++) {
        int op;
        cin >> op;
        if (op == 1) {
            cin >> p[i][0];
            dep[i] = dep[p[i][0]] + 1;
            for (int j = 0; (1 << j) <= dep[i]; j++) {
                p[i][j + 1] = p[p[i][j]][j];
            }
            cin >> a[i] >> c[i];
        } else {
            int v, w;
            cin >> v >> w;
            long long amount = 0, cost = 0;
            while (w > 0 and a[v] > 0) {
                int u = v;
                for (int j = N - 1; j >= 0; j--) {
                    if (p[u][j] != -1 and a[p[u][j]] > 0) {
                        u = p[u][j];
                    }
                }
                int mi = min(w, a[u]);
                w -= mi, a[u] -= mi;
                amount += mi, cost += 1LL * mi * c[u];
            }
            cout << amount << ' ' << cost << endl;
        }
    }
    return 0;
}