Codeforces Round 1047 (Div. 3)

链接:https://codeforces.com/contest/2137

A. Collatz Conjecture

    这题读假了以为是找出全部的可能值。

算法:

    模拟。

思路:

    无。

关键代码:

void solve()
{
    ll k, x;
    cin >> k >> x;

    cout << (x << k) << endl;
}

B. Fun Permutation

算法:

    构造,数学。

思路:

    玩几组样例可以发现规律在 \(n\) 的值上,将 \( n \)转换为三进制,其结果只有 \(0,1,2\) 三种情况:

  • 当值为 \(0\) 或 \(1\) 时,两两之间全部构造成 \(3\) 的倍数即可。
  • 当值为 \(2\) 时,两两之间全部构造成 \(4\) 的倍数即可。

关键代码:

void solve()
{
    int n;
    cin >> n;
    vector<int> p(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> p[i];

    if (n % 3 == 0 || (n + 1) % 3 == 0)
    {
        for (int i = 1; i <= n; ++i)
        {
            if (p[i] % 3 == 0)
                cout << p[i] << ' ';
            else
            {
                if ((p[i] + 1) % 3 == 0)
                    cout << p[i] - 1 << ' ';
                else
                    cout << p[i] + 1 << ' ';
            }
        }
    }
    else
    {
        for (int i = 1; i <= n; ++i)
            cout << n + 1 - p[i] << ' ';
    }

    cout << endl;
}

C. Maximum Even Sum

算法:

    打表,找规律。

思路:

    直接打表即可。

关键代码:

void solve()
{
    ll a, b;
    cin >> a >> b;

    if (((a & 1) && (b - 2) % 4 == 0) || (a % 2 == 0 && (b & 1)))
    {
        cout << -1 << endl;
        return;
    }

    if ((a & 1) && (b & 1))
        cout << a * b + 1 << endl;
    else
        cout << a * (b / 2) + 2 << endl;
}

D. Replace with Occurrences

算法:

    构造。

思路:

    \(b\) 中相同的值构造成一样的元素,注意一下大小即可。

关键代码:

void solve()
{
    ll n;
    cin >> n;
    vector<ll> b(n);
    for (auto &x : b)
        cin >> x;

    map<int, vector<int>> m;
    for (int i = 0; i < n; ++i)
        m[b[i]].push_back(i);

    vector<int> ans(n);
    ll val = 1;
    for (auto [num, v] : m)
    {
        ll cnt = v.size();

        if (cnt % num)
        {
            cout << -1 << endl;
            return;
        }

        for (auto x : v)
        {
            ans[x] = val;
            --cnt;

            if (cnt % num == 0)
                ++val;
        }
    }

    for (auto x : ans)
        cout << x << ' ';
    cout << endl;
}

E. Mexification

算法:

    模拟,数学。

思路:

    通过打表可以发现,操作存在周期性,且周期性都为 \(2\),但是有一些特殊的数组其周期性是在 \(3\) 才开始展现,既第 \(1\) 次操作和第 \(3\) 次操作得到的数组是不一样的,如:\(0 1 1 2 3\)。

    所以不能无脑奇数执行一次操作,偶数执行两次操作,需要做出特判,这里直接选择在 \(k\) 大于 \(1\) 且为奇数时执行三次,\(k\) 为 \(1\) 时还是执行 \(1\) 次, 所以总的时间复杂度为 \(O(n)\)。

    对于每次操作中每个元素的更新:

  • 先对整个数组统计每个元素出现的次数,然后求出全体 \(mex\)。
  • 对于每个元素,如果其只出现了一次,其可能对答案造成影响,既:
    • 如果 \(a[i] < mex\),那么对于其他所有元素的 \(mex\) 会在 \(a[i]\) 处断开,此时 \(a[i] = a[i]\)。
    • 如果 \(a[i] > mex\),那么不会对答案照成影响,\(a[i] = mex\)。
  • 如果出现了多次,其值就为 \(mex\)。

关键代码:

void solve()
{
    int n, k;
    cin >> n >> k;
    vector<ll> a(n);
    for (auto &x : a)
        cin >> x;

    if (k > 1)
        k = k % 2 + 2;

    for (int tt = 0; tt < k; ++tt)
    {
        vector<ll> cnt(n + 1, 0);
        for (int i = 0; i < n; ++i)
            ++cnt[a[i]];

        ll mex = 0;
        while (cnt[mex])
            ++mex;

        for (int i = 0; i < n; ++i)
        {
            if (cnt[a[i]] == 1)
                a[i] = min(mex, a[i]);
            else
                a[i] = a[i];
        }
    }

    cout << accumulate(all(a), 0ll) << endl;
}

F. Prefix Maximum Invariance

算法:

    贡献法,贪心,堆。

思路:

    题目要求计算一个全区间函数的总和,这类问题通常可以通过贡献法来解决。

     核心思路是:枚举每个位置 \(i\),找出它在所有区间中的贡献,再累加起来。

情况一:当 \(a_i = b_i\)

    此时在任何包含 \(i\) 的区间中,都可以令 \(z_i = a_i = b_i\),不需要额外条件。

    它的贡献就是所有区间数目:

  • 左端点可以从\(1 \ldots i\) 任意选择(共 \(i\) 种)。
  • 右端点可以从 \(i \ldots n\) 任意选择(共 \(n – i + 1\) 种)。

    所以总贡献为: \(i \times (n – i + 1)\)

情况二:当 \(a_i \neq b_i\)

    设 \(need = \max(a_i, b_i)\)。
    为了让 \(z_i = b_i\) 且保持前缀最大值与 \(a\) 一致,必须在 \(i\) 之前存在一个位置 \(pos\),满足: \(a_{pos} \ge need\)

    这样 \(z_i\)​ 才能合法取到 \(b_i\)。

    一旦找到这样的最早位置 \(pos\),贡献就由它来决定:

  • 左端点可选 \([1 \ldots pos]\),数量为 \(pos\)。
  • 右端点可选 \([i \ldots n]\),数量为 \((n – i + 1)\)。

    因此贡献为: \(pos \times (n – i + 1)\)

实现方法

  • 先把所有 \(a_i = b_i\) 的情况直接累加贡献。
  • 对于 \(a_i \neq b_i\)​,记录需求 (need, i),其中 \(need = \max(a_i, b_i)\),表示“位置 \(i\) 需要一个 \(\ge need\) 的前缀”。
  • 使用小根堆维护这些需求,并从右向左遍历数组:
    • 当扫到某个位置 \(i\) 时,如果 \(a_i \ge\) 堆顶的 \(need\),说明该位置可以作为这批需求的最早前缀位置。
    • 由于是小根堆,满足条件的需求会连续出现在堆顶,所以可以用 while 循环批量弹出并计算贡献。
    • 每个弹出的需求 (need, y) 贡献为: \(i \times (n – y + 1)\)

    这样,就能在 \(O(n \log n)\) 时间内完成所有贡献的计算。

关键代码:

void solve()
{
    using namespace views;

    int n;
    cin >> n;
    vector<int> a(n + 1), b(n + 1);
    for (auto &x : a | drop(1))
        cin >> x;
    for (auto &x : b | drop(1))
        cin >> x;

    ll ans = 0;
    for (int i = 1; i <= n; ++i)
        ans += (a[i] == b[i]) * (i * (n - i + 1ll));

    priority_queue<pii, vector<pii>, greater<pii>> q;
    for (int i = n; i; --i)
    {
        while (q.size() && q.top().first <= a[i])
        {
            auto [x, y] = q.top();
            q.pop();

            ans += i * (n - y + 1ll);
        }

        if (a[i] == b[i])
            continue;

        q.push({max(a[i], b[i]), i});
    }

    cout << ans << endl;
}

G. Cry Me a River

    还没补。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇