Codeforces Round 960 (Div. 2)(A - D)

value0發表於2024-07-21

Codeforces Round 960 (Div. 2)(A - D)

A - Submission Bait

解題思路:

假設直接選最大數,如果最大數有奇數個,\(Alice\)必勝,反之必敗。

根據這個思路,從大到小看數字,找到第一個出現奇數次的數,從它開始選,就能保證每次\(Alice\)選的時候還剩奇數個選項。

程式碼:

#include <bits/stdc++.h>
#include <cxxabi.h>
using namespace std;
using ll = long long;
using i128 = __int128_t;
using ull = unsigned long long;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
#define fi first
#define se second
const int N = 2e5 + 10;
const int M = 60;
const ll inf = 1ll << 30;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l, int r) { return rng() % (r - l + 1) + l; }

void solve()
{
    int n;
    cin >> n;
    vector<int> cnt(n + 10);
    for (int i = 1; i <= n; i++)
    {
        int t;
        cin >> t;
        cnt[t]++;
    }
    for (int i = n; i; i--)
    {
        cnt[i] += cnt[i + 1];
        if (cnt[i] & 1)
        {
            cout << "YES\n";
            return;
        }
    }
    cout << "NO\n";
}

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

B - Array Craft

解題思路:

整個序列分為三段\([1, y + 1],[y, x],[x + 1, n]\)

\([x,y]\)全部填正數,這樣只要加上這一段一定比不加要更大。

對於最大字首和來說,\([x + 1, n]\)區間中\(sum(x + 1,... ,k)\in [x + 1, n]\)一定得小於等於\(0\)。先考慮能否取儘量小?不方便構造,因為如果儘量往小取,我們還要反過來考慮\(suf[y, ...,n] >suf[x - 1, ...n]\)。所以,考慮使得字尾和為零。

如果\(n - (x + 1)\)為偶數,我們按\(-1, 1\)的規律一定能讓其最後和為零。

如果\(n - (x + 1)\)是奇數,我們可以最終讓區間和為\(-1\)。發現\([x,y]\)區間全部取\(1\)區間和都有\(2\),一定大於\(-1\),滿足字尾和條件,所以可以這麼構造。

對於區間\([1, y + 1]\)同理考慮。

程式碼:

#include <bits/stdc++.h>
#include <cxxabi.h>
using namespace std;
using ll = long long;
using i128 = __int128_t;
using ull = unsigned long long;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
#define fi first
#define se second
const int N = 2e5 + 10;
const int M = 60;
const ll inf = 1ll << 30;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l, int r) { return rng() % (r - l + 1) + l; }

void solve()
{
    int n, x, y;
    cin >> n >> x >> y;
    vector<int> a(n + 1);
    int l = 1;
    int r = y - 1;
    int len = r - l + 1;
    if (len & 1)
    {
        int cur = -1;
        for (int i = l; i <= r; i++)
        {
            a[i] = cur;
            cur *= -1;
        }
    }
    else
    {
        int cur = 1;
        for (int i = l; i <= r; i++)
        {
            a[i] = cur;
            cur *= -1;
        }
    }
    for (int i = y; i <= x; i++)
    {
        a[i] = 1;
    }
    l = x + 1;
    r = n;
    int cur = -1;
    for (int i = l; i <= r; i++)
    {
        a[i] = cur;
        cur *= -1;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << a[i] << " \n"[i == n];
    }
}

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

C - Mad MAD Sum

解題思路:

舉例\((1,1,1,2,2,2)\rightarrow (0,1,1,1,2,2) \rightarrow (0,0,1,1,1,2) \rightarrow (0,0,0,1,1,1)\)

像這樣玩一下,不難發現,變化後的序列一定是一個單調遞增的序列,且每一段相同數字的變化都是有跡可循的。

比如,最大數段會不斷變短,前面數段會不斷右移。

考慮到初始序列不具備這樣的性質,所以可以先對原序列進行兩次操作,然後對第二次操作後的單調序列按照上述性質模擬即可。

兩次操作後,出了最大數段,不可能出現數段長度為\(1\)的情況。這個情況處理起來麻煩且會出現在前兩次操作中,所以直接操作掉即可。

程式碼:

#include <bits/stdc++.h>
#include <cxxabi.h>
using namespace std;
using ll = long long;
using i128 = __int128_t;
using ull = unsigned long long;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
#define fi first
#define se second
const int N = 2e5 + 10;
const int M = 60;
const ll inf = 1ll << 30;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l, int r) { return rng() % (r - l + 1) + l; }

void solve()
{
    ll n;
    cin >> n;
    ll ans = 0;
    vector<ll> a(n + 1), pos(n + 1), cnt(n + 1);
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        a[i] = x;
        ans += x;
    }
    ll mx = 0;
    for (int i = 1; i <= n; i++)
    {
        cnt[a[i]]++;
        if (cnt[a[i]] == 2)
        {
            pos[a[i]] = i;
        }
        if (a[i] > mx && cnt[a[i]] >= 2)
        {
            mx = a[i];
        }
        a[i] = mx;
        ans += a[i];
        // cout << i << ' ' << a[i] << endl;
    }
    for (int i = 1; i <= n; i++)
    {
        cnt[i] = pos[i] = 0;
    }
    for (int i = 1; i <= n; i++)
    {
        cnt[a[i]]++;
        if (cnt[a[i]] == 2)
        {
            pos[a[i]] = i;
        }
    }
    ll r = n;
    for (ll i = n; i; i--)
    {
        if (cnt[i] < 2 || pos[i] > r)
        {
            continue;
        }
        ll m = r - pos[i] + 1;
        ans += (m * (m + 1)) / 2 * i;
        if (r != n && m > 1)
        {
            ans += (n - 1 - r + 1) * m * i;
        }
        r = pos[i] - 1;
    }
    cout << ans << '\n';
}

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

D - Grid Puzzle

解題思路:

操作次數上限為\(n\),就是一行一行地變白。嘗試用\(2 \times 2\)塗白最佳化。

舉例:

  • \((1, 1),(1, 2),(2, 1),(2, 2)\):都可以一次塗白兩行,運算元為一。由此也可見,這裡\(1\)\(2\)其實是等價的,少的那個\(1\)並無最佳化空間。
  • \((2, 4, 4, 4,4, 2)\):這種兩端\(2\)中間連續的\(4\),運算元都可以最佳化到行數減一。同理,把中間連續的\(4\)部分換成\(3\)也行,\(3,4\)在這裡也是等價的。
  • 這樣我們就還可以把\((2, 2)\)這種看作中間連續的\(4\)數量為\(0\)的情況。

列舉下標,只有當前\(a_i = 2\)時,我們可以嘗試最佳化運算元。其中\((1, 2)\)等價\((3, 4)\)等價。其餘操作,都是直接將當前行塗白最優。

注意:手畫一下就能發現,中間\(4\)的個數一定得是偶數個才能嘗試最佳化。

程式碼:

#include <bits/stdc++.h>
#include <cxxabi.h>
using namespace std;
using ll = long long;
using i128 = __int128_t;
using ull = unsigned long long;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
#define fi first
#define se second
const int N = 2e5 + 10;
const int M = 60;
const ll inf = 1ll << 30;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l, int r) { return rng() % (r - l + 1) + l; }

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n + 1), dp(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        // 等價
        if (a[i] == 1)
        {
            a[i] = 2;
        }
        // 等價
        if (a[i] == 3)
        {
            a[i] = 4;
        }
    }
    int len = 0;
    for (int i = 1; i <= n; i++)
    {
        dp[i] = dp[i - 1] + (a[i] == 0 ? 0 : 1);
        // 只有2結尾有可能最佳化操作次數
        if (a[i] == 2 && a[i - len - 1] == 2 && len % 2 == 0)
        {
            dp[i] = min(dp[i], dp[i - len - 2] + len + 1);
        }
        if (a[i] == 4)
        {
            len++;
        }
        else
        {
            len = 0;
        }
    }
    cout << dp[n] << endl;
}

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

相關文章