Codeforces 969 div2[A~E] 个人题解

目录

A - Dora's Set

原题链接

思路分析

AC代码

B - Index and Maximum Value

原题链接

思路分析

AC代码

C - Dora and C++

原题链接

思路分析

AC代码

D - Iris and Game on the Tree

原题链接

思路分析

AC代码

E - Iris and the Tree

原题链接

思路分析

AC代码



A - Dora's Set

原题链接

A - Dora's Set

思路分析


显然不能选两个偶数

比较简单的结论:
任意相邻的两个数一定互质

任意相邻的两个奇数一定互质

于是得出:

那么两个奇数 中间 夹一个偶数一定合法,并且这样得到的三元组是最多的

时间复杂度:O(N),当然可以做到O(1)

AC代码

#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr int P = 1'000'000'007;


void solve() {
    int l, r;
    std::cin >> l >> r;

    int res = 0;
    for (int i = l & 1 ? l : l + 1; i + 1 < r; i += 4) {
        ++ res;
    }

    std::cout << res << '\n';
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t = 1;
    std::cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

B - Index and Maximum Value

原题链接

B - Index and Maximum Value

思路分析

我们注意到一个事情,由于每次操作都是对值域内的元素操作

那么从始至终,我们数组最大元素的下标始终不变(自己手玩一下就明白了)

时间复杂度:O(N)

AC代码

#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr int P = 1'000'000'007;

void solve() {
    int n, m;
    std::cin >> n >> m;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) std::cin >> a[i];

    int ma = *std::max_element(a.begin(), a.end());
    
    for (int i = 0; i < m; ++ i) {
        char op;
        int l, r;
        std::cin >> op >> l >> r;
        if (l <= ma && ma <= r)
            ma += op == '+' ? 1 : -1;
        std::cout << ma << " \n"[i + 1 == m];
    }
    
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t = 1;
    std::cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

C - Dora and C++

原题链接

C - Dora and C++

思路分析

根据裴蜀定理:给定x,y存在ax + by = gcd(x, y)

那么我们可以实现对每个元素任意次 gcd(a, b) 加减操作

那么我们一定可以将原数组的元素都调整到 [0, gcd(a, b)) 内

显然我们可以得到的 range 不会超过 gcd(a, b)

我们将调整后的数组排序,枚举最小值 c[i]

为什么最小值可以取原数组的元素?

因为任意最优解我们都可以通过整个数组加减gcd来使得其最小值落在[0, gcd(a, b))内

那么枚举最小值后,最大值是哪个?

对于最小值c[i],最大值一定是c[i - 1] + gcd

很明显,不做证明

时间复杂度:O(NlogN)

AC代码

#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr int P = 1'000'000'007;

void solve() {
    int n, a, b;

    std::cin >> n >> a >> b;
    int g = std::gcd(a, b);

    std::vector<int> c(n);
    for (int i = 0; i < n; ++ i) std::cin >> c[i], c[i] %= g;

    std::ranges::sort(c);

    int res = c[n - 1] - c[0];

    for (int i = 1; i < n; ++ i) {
        res = std::min(res, c[i - 1] + g - c[i]);
    }

    std::cout << res << '\n';
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t = 1;
    std::cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

D - Iris and Game on the Tree

原题链接

D - Iris and Game on the Tree

思路分析

看起来很复杂,我们不妨先考虑比较好想的情况:根节点值确定

那么我们 Iris 一定将叶子尽可能染成和树根相反的值,Dora 一定尽可能将叶子染成和树根相同的值

记叶子中原来0的数目为c0,1的数目为c1,?为none

那么当root = 0时,Iris的收益就是 (none + 1) / 2 + c1

root=1不再赘述

当根节点值不确定:

这个时候我们发现,如果Iris先手调根,那么不管调几,Dora总能让Iris的收益减少

所以Iris要晚调根,同样的,Dora也要晚调根

那么当非叶子结点存在 ?时,二者都会先去调这些结点

这些结点数目的奇偶性决定了二者谁先手调根

所以我们分为两种情况,Iris先手调根:收益就是 max(c0, c1) + none / 2

当 可以使Dora先手的时候,收益是:min(c0, c1) + (none + 1) / 2

第二种情况可以取的时候需要和第一种情况取max

时间复杂度:O(N)

AC代码

#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr int P = 1'000'000'007;

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> d(n);

    for (int i = 1, u, v; i < n; ++ i) {
        std::cin >> u >> v;
        -- u, -- v;
        ++ d[u], ++ d[v];
    }

    std::string s;
    std::cin >> s;

    int c0 = 0, c1 = 0, f = 0, none = 0;

    for (int i = 1; i < n; ++ i) {
        if (d[i] == 1) {
            if (s[i] == '0') ++ c0;
            else if(s[i] == '1') ++ c1;
            else ++ none;
        }
        else if (s[i] == '?') {
            f ^= 1;
        }
    }

    if (s[0] == '?') {
        int res = std::max(c0, c1) + none / 2;
        if (f)
            res = std::max(res, std::min(c0, c1) + (none + 1) / 2);
        std::cout << res << '\n';
    }
    else {
        if (s[0] == '1') 
            std::cout << c0 + (none + 1) / 2 << '\n';
        else       
            std::cout << c1 + (none + 1) / 2 << '\n';
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t = 1;
    std::cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

E - Iris and the Tree

原题链接

E - Iris and the Tree

思路分析

对于每个path,其收益为 w - 路径外已经确定的边权和

但是要进行特判:如果路径上边全部确定,那么其值就是路径权值和

然后可以注意到每条边在所有路径中一共出现两次

初始时,收益为n * w

最一般的情况:后面每增加一条边,除了该边出现的两条路径,所有的路径的值都会- 2f

如果考虑已经满了的路径,我们发现我们的收益就是:

已满路径的带权长度和 + 未满路径条数 * (w - 已出现路径长度和) + Σ未满路径上已出现的边权和

那么对于一条边<p[u], u>如何找到两条路径的端点?

u - 1 和 u所在子树最大dfs序,第二个我们逆着dfs序进行递推即可

由于给的是dfs序,lca(i, (i + 1) % n) = fa(i + 1),所以我们可以很容易处理出每条路径的边的数目

时间复杂度:O(N)

AC代码

#include <bits/stdc++.h>

using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;

constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr int P = 1'000'000'007;


void solve() {
    int n;
    i64 w;
    std::cin >> n >> w;

    std::vector<int> p(n), d(n), res(n), submax(n);
    std::vector<std::vector<int>> adj(n);

    for (int i = 1; i < n; ++ i) {
        std::cin >> p[i], -- p[i];
        adj[p[i]].push_back(i);
    }

    for (int u = 0; u < n; ++ u)
        for (int v : adj[u])
            d[v] = d[u] + 1;

    for (int i = 0; i + 1 < n; ++ i) 
        res[i] = d[i] + d[i + 1] - d[p[i + 1]] * 2;
    res[n - 1] = d[n - 1];

    std::iota(submax.begin(), submax.end(), 0);
    for (int i = n - 1; i; -- i)
        submax[p[i]] = std::max(submax[i], submax[p[i]]);

    std::vector<i64> len(n);
    i64 ans = 0, sum = 0, o = 0;
    int cnt = 0;

    for (int i = 1, v; i < n; ++ i) {
        i64 t;
        std::cin >> v >> t;
        -- v;
        int u = v - 1, sub = submax[v];
        sum += t;
        len[u] += t, len[sub] += t;
        o += 2 * t;

        if (!-- res[u]) {
            ans += len[u];
            o -= len[u];
            ++ cnt;
        }

        if (! -- res[sub]) {
            ans += len[sub];
            o -= len[sub];
            ++ cnt;
        }

        std::cout << ans + (n - cnt) * (w - sum) + o << " \n"[i + 1 == n];
    }

}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int t = 1;
    std::cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值