CF Educational Round7 个人题解

CF Educational Round7 题解

A

题意

有一个无穷序列 \([1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ...]\),求这里面的第 \(n(1 \le n \le 10^{14}\)

思路

刚看到还以为是什么很难的推式子,结果发现对于每一个从 \(1\) 开始的连续数字串,长度分别为 \(1, 2, 3, 4, ...\),所以直接枚举的话到了第 \(k\) 个数字串所枚举出来的总长度是 \(O(k^2)\) 的,所以直接这么枚举到第 \(n\) 个位置,时间复杂度是 \(O(\sqrt n)\)

代码

// A. Infinite Sequence
// 1000 ms
// 256 MB
// https://codeforces.com/contest/622/problem/A
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int __stt = clock();
int tests = 1;

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

    i64 now = 1;
    i64 cnt = 1;
    while (now <= n) {
        now += cnt;
        ++cnt;
    }

    now -= cnt - 1;
    std::cout << n - now + 1 << '\n';
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
#ifdef LOCAL
    int __edt = clock();
    std::cerr << "Time: " << (__edt - __stt) << "ms\n";
#endif
    return 0;
}

B

题意

给定一个二十四小时制的时间,计算 \(a\) min 后是几点

思路

直接按分钟模 \(60\),小时模 \(24\) 计算就行了,复杂度 \(O(1)\)

但是这道题的输入比较难写,我们可以使用 scanf 辅助,通过此题,也学习到一个有用的成员函数

[string].c_str,也就是说任意一个 string 类型的变量都有一个成员函数 c_str 将其转化为 C 风格的字符串,这样就可以用 %s 输出 string 了

代码

// B. The Time
// 1000 ms
// 256 MB
// https://codeforces.com/contest/622/problem/B
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int __stt = clock();
int tests = 1;

void solve() {
    int h, m, a;
    scanf("%d:%d\n%d", &h, &m, &a);

    // std::cerr << h << ' ' << m << ' ' << a << '\n';

    int add_hour = (m + a) / 60;
    m = (m + a) % 60;
    h = (h + add_hour) % 24;

    std::string ans1 = std::to_string(h);
    std::string ans2 = std::to_string(m);
    if (ans1.size() == 1)
        ans1 = "0" + ans1;
    if (ans2.size() == 1)
        ans2 = "0" + ans2;

    printf("%s:%s", ans1.c_str(), ans2.c_str());
}

int main() {
    // std::ios::sync_with_stdio(0);
    // std::cin.tie(0);
    // std::cout.tie(0);
    while (tests--) {
        solve();
    }
#ifdef LOCAL
    int __edt = clock();
    std::cerr << "Time: " << (__edt - __stt) << "ms\n";
#endif
    return 0;
}

C

题意

给定一个长为 \(n\) 的 1-base 数组 \(a\),有 \(m\) 次询问,每次询问需要回答出一个满足 \(l \le i \le r\)\(a_i \neq x\)\(i\) 或者确定该区间没有满足条件的数

思路

直接找肯定是超时的,我们发现这里只用找到任意一个 \(\neq x\) 的数,所以对于每一个数 \(a_i\),我们去维护一个 \(pos_i\),定义其为满足 \(a_{pos_i} \sim a_i\) 全都是 \(a_i\) 的最靠前的位置

那么我们就有

\[pos_i = \left\{ \begin{aligned} pos_{i - 1} &, & a_i = a_{i - 1}, \\ i &, &a_i \neq a_i - 1. \end{aligned} \right. \]

对于每一个 \(l, r, x\) 的询问,我们查看 \(a_r\) 是否等于 \(x\)

  • 如果不等于,我们直接输出 \(r\)

  • 如果等于,我们看 \(pos_r\) 是否 \(>l\)

    • 如果大于,输出 \(pos_r - 1\)
    • 如果不大于, 输出 \(-1\) (无解)

这是因为 \(pos\) 的优秀性质:对于一个小标 \(i\),因为 \(pos_i\) 是连续段内最靠左的与 \(a_i\) 相同的数,所以 \(pos_{i - 1}\) 一定不与 \(a_i\) 相同

总时间复杂度 \(O(max(n, m))\)

代码

// C. Not Equal on a Segment
// 1000 ms
// 256 MB
// https://codeforces.com/contest/622/problem/C
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int __stt = clock();
int tests = 1;

void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n + 1), pos(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
        pos[i] = (a[i] == a[i - 1] ? pos[i - 1] : i);
    }

    for (int i = 0; i < m; ++i) {
        int l, r, x;
        std::cin >> l >> r >> x;
        if (a[r] == x) {
            if (pos[r] <= l)
                std::cout << -1 << '\n';
            else
                std::cout << pos[r] - 1 << '\n';
        } else {
            std::cout << r << '\n';
        }
    }
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
#ifdef LOCAL
    int __edt = clock();
    std::cerr << "Time: " << (__edt - __stt) << "ms\n";
#endif
    return 0;
}

D

题意

一个长度为 \(2n\) 的双排列是指从 \(1 \sim n\) 的每个数字出现两次。 若一个数字 \(i\) 两次出现的位置为 \(a, b\),则定义 \(d_i\)\(|b - a|\),即这两个数的距离

构造一个长度为 \(n\) 的双排列,使得 \(\sum_{i = 1}^n (n - i) \cdot |d_i + i - n|\) 的值最小

思路

这类最小化的构造题,先考虑能不能满足理论最小值

对于原式,我们发现 \(n - i \ge 0\)\(|d_i + i - n| \ge 0\),所以原式理论最小值就是 0

也就是说每个 \(i\)\(n - i = 0\) 或者 \(i - n = -d_i \Leftrightarrow n - i = d_i\)

前者意味着两个 \(n\) 可以随便填,我们考虑后者。这一步思考是可以得到的,但是不太好说明,较为感性,所以我们打个表或者手算一下小样例

比方说 \(n = 5\) 时 答案为 \(1 3 5 3 1 2 4 4 2 5\),这里面我们发现两个 \(5\) 分别起到了一个占位的作用和一个将一部分数的距离由偶数变成奇数的作用

依次我们推敲答案应该为一个奇数部分组成的回文串+一个偶数部分组成的回文串,并且若 \(n\) 为奇数,奇数部分回文串中间要加上一个 \(n\) 调整距离,\(n\) 为偶数则偶数部分回文串中间要加一个 \(n\) 调整距离

这就是答案

复杂度 \(O(n)\)

代码

// D. Optimal Number Permutation
// 1000 ms
// 256 MB
// https://codeforces.com/contest/622/problem/D
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int __stt = clock();
int tests = 1;

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> v(n << 1 | 1, 0);

    for (int i = 1; i <= n / 2; ++i) {
        v[i] = i * 2 - 1;
        v[n - i + 1] = i * 2 - 1;
    }

    for (int i = 1; i <= n / 2; ++i) {
        v[i + n] = i * 2;
        v[n * 2 - i] = i * 2;
    }

    for (int i = 1; i <= n * 2; ++i) {
        if (v[i] != 0)
            std::cout << v[i] << ' ';
        else
            std::cout << n << ' ';
    }
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
#ifdef LOCAL
    int __edt = clock();
    std::cerr << "Time: " << (__edt - __stt) << "ms\n";
#endif
    return 0;
}

E

题意

给定一颗 \(n\) 个节点的有根树,根位于节点 \(1\)

每个叶子节点有一只蚂蚁,每一秒,蚂蚁可以向父节点走一步,同一时刻除根节点外不能存在一个节点有两只蚂蚁,最少需要多少秒才能使所有蚂蚁到达根节点

思路

一个树形 DP 的好题,但现在好像已经变成一个水题了

正难则反(虽然正着好像也不难),最开始所有蚂蚁都在根节点,他们现在要走到每个叶子节点。如果不考虑每个节点只能有一个蚂蚁,答案肯定是所有叶子节点深度的最大值

简单理解一下,最优情况一定是一只蚂蚁跟着一只蚂蚁。所以为了让这类情况尽可能多,对于同一颗子树,我们会先让深度越深的蚂蚁先走,一秒触发一个,计算出每个节点的深度然后排序,直接暴力计算时间+深度作为这个点的代价,找到最大值即为答案

时间复杂度 \(O(n log n)\)

代码

// E. Ants in Leaves
// 2000 ms
// 256 MB
// https://codeforces.com/contest/622/problem/E
#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int __stt = clock();
int tests = 1;

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> d(n + 1, 0);
    std::vector<std::vector<int>> adj(n + 1);
    for (int i = 1, u, v; i < n; ++i) {
        std::cin >> u >> v;
        adj[u].emplace_back(v);
        adj[v].emplace_back(u);
    }

    int ans = 0;

    for (int i = 0; i < adj[1].size(); ++i) {
        int c = 0;
        auto rec = [&](int w, int p, int h, auto&& self) -> void {
            int fg = 1;
            for (const auto v : adj[w]) {
                if (v == p)
                    continue;
                fg = 0;
                self(v, w, h + 1, self);
            }
            if (fg)
                d[++c] = h;
        };
        rec(adj[1][i], 1, 1, rec);
        std::sort(d.begin() + 1, d.begin() + c + 1);
        for (int j = 2; j <= c; ++j)
            d[j] = d[j] > d[j - 1] + 1 ? d[j] : d[j - 1] + 1;
        ans = ans > d[c] ? ans : d[c];
    }

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

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
#ifdef LOCAL
    int __edt = clock();
    std::cerr << "Time: " << (__edt - __stt) << "ms\n";
#endif
    return 0;
}

F

题目

给定 \(n, k(1 \le n \le 10 ^ 9, 1 \le k \le 10 ^ 6)\),求 \(\sum_{i = 1}^{n} i^k\)

思路

多项式,拉插,***数,会不了一点,后面再补

posted @ 2026-02-09 14:42  Billlly  阅读(18)  评论(0)    收藏  举报