Codeforces Round 984 div3 個人題解(A~F)

ExtractStars發表於2024-11-03

Codeforces Round 984 div3 個人題解(A~F)

Dashboard - Codeforces Round 984 (Div. 3) - Codeforces

火車頭

#define _CRT_SECURE_NO_WARNINGS 1

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <chrono>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

#define ft first
#define sd second

#define yes cout << "yes\n"
#define no cout << "no\n"

#define Yes cout << "Yes\n"
#define No cout << "No\n"

#define YES cout << "YES\n"
#define NO cout << "NO\n"

#define pb push_back
#define eb emplace_back

#define all(x) x.begin(), x.end()
#define all1(x) x.begin() + 1, x.end()
#define unq_all(x) x.erase(unique(all(x)), x.end())
#define unq_all1(x) x.erase(unique(all1(x)), x.end())
#define sort_all(x) sort(all(x))
#define sort1_all(x) sort(all1(x))

#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3fLL

#define RED cout << "\033[91m"     // 紅色
#define GREEN cout << "\033[92m"   // 綠色
#define YELLOW cout << "\033[93m"  // 藍色
#define BLUE cout << "\033[94m"    // 品紅
#define MAGENTA cout << "\033[95m" // 青色
#define CYAN cout << "\033[96m"    // 青色
#define RESET cout << "\033[0m"    // 重置

template <typename T>
void Debug(T x, int color = 1)
{
    switch (color)
    {
    case 1:
        RED;
        break;
    case 2:
        YELLOW;
        break;
    case 3:
        BLUE;
        break;
    case 4:
        MAGENTA;
        break;
    case 5:
        CYAN;
        break;
    default:
        break;
    }
    cout << x;
    RESET;
}

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
// typedef __int128_t i128;

typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pdd;
typedef pair<ll, int> pli;
typedef pair<string, string> pss;
typedef pair<string, int> psi;
typedef pair<string, ll> psl;

typedef tuple<int, int, int> ti3;
typedef tuple<ll, ll, ll> tl3;
typedef tuple<ld, ld, ld> tld3;

typedef vector<bool> vb;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<string> vs;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef vector<pli> vpli;
typedef vector<pss> vpss;
typedef vector<ti3> vti3;
typedef vector<tl3> vtl3;
typedef vector<tld3> vtld3;

typedef vector<vi> vvi;
typedef vector<vl> vvl;

typedef queue<int> qi;
typedef queue<ll> ql;
typedef queue<pii> qpii;
typedef queue<pll> qpll;
typedef queue<psi> qpsi;
typedef queue<psl> qpsl;

typedef priority_queue<int> pqi;
typedef priority_queue<ll> pql;

typedef map<int, int> mii;
typedef map<int, bool> mib;
typedef map<ll, ll> mll;
typedef map<ll, bool> mlb;
typedef map<char, int> mci;
typedef map<char, ll> mcl;
typedef map<char, bool> mcb;
typedef map<string, int> msi;
typedef map<string, ll> msl;
typedef map<int, bool> mib;

typedef unordered_map<int, int> umii;
typedef unordered_map<ll, ll> uml;
typedef unordered_map<char, int> umci;
typedef unordered_map<char, ll> umcl;
typedef unordered_map<string, int> umsi;
typedef unordered_map<string, ll> umsl;

std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());

template <typename T>
inline T read()
{
    T x = 0;
    int y = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0')
    {
        if (ch == '-')
            y = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return x * y;
}

template <typename T>
inline void write(T x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x >= 10)
    {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

/*#####################################BEGIN#####################################*/
void solve()
{
}

int main()
{
    ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    // freopen("test.in", "r", stdin);
    // freopen("test.out", "w", stdout);
    int _ = 1;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

/*######################################END######################################*/
// 連結:

A. Quintomania

鮑里斯-諾特金創作旋律。他用音符序列來表示旋律,其中每個音符都被編碼為一個從 0 到 127 的整數。兩個音符 \(a\)\(b\) 之間的間隔等於 \(|a−b|\) 個半音。

如果每兩個相鄰音符之間的音程是 5 個半音或 7 個半音,Boris 就會認為旋律是完美的。

在創作完最新的旋律後,他熱情地向您展示了他的作品集。幫助鮑里斯-諾特金瞭解他的旋律是否完美。

輸入
第一行包含一個整數 \(t\) \((1 \leq t \leq 1000)\) - 旋律的數量。

每首旋律由兩行描述。

第一行包含一個整數 \(n\) \((2 \leq n \leq 50)\) --旋律中的音符數。

第二行包含 \(n\) 個整數 \(a_1, a_2, \ldots, a_n\) \((0 \leq a_i \leq 127)\) --旋律的音符。

輸出
對於每段旋律,如果完美,則輸出 "YES";否則,輸出 "NO"。

您可以用任何大小寫(大寫或小寫)輸出答案。例如,字串 "yEs"、"yes"、"Yes "和 "YES "將被識別為肯定回答。

示例
輸入

8
2
114 109
2
17 10
3
76 83 88
8
38 45 38 80 85 92 99 106
5
63 58 65 58 65
8
117 124 48 53 48 43 54 49
5
95 102 107 114 121
10
72 77 82 75 70 75 68 75 68 75

輸出

YES
YES
YES
NO
YES
NO
YES
YES

解題思路

列舉檢查每兩個\(a\)之間是否符合要求即可

程式碼實現

void solve()
{
    int n;
    cin >> n;
    vi a(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i < n; i++)
    {
        if (abs(a[i] - a[i - 1]) != 5 && abs(a[i] - a[i - 1]) != 7)
        {
            NO;
            return;
        }
    }
    YES;
}

B. Startup

阿爾謝尼想出了另一個商業計劃--用自動售貨機賣汽水!為此,他購買了一臺有 \(n\) 個貨架和 \(k\) 個瓶子的自動售貨機,其中第 \(i\) 個瓶子的品牌指數為 \(b_i\),成本為 \(c_i\)

您可以在每個貨架上放置任意數量的瓶子,但同一貨架上的所有瓶子必須是同一品牌。

Arseniy 知道他放在機器貨架上的所有瓶子都會賣掉。因此,他要求您計算出他能賺取的最大金額。

輸入
第一行包含一個整數 \(t\) \((1 \leq t \leq 10^4)\) - 測試用例數。

每個測試用例的第一行包含兩個整數 \(n\)\(k\) \((1 \leq n, k \leq 2 \cdot 10^5)\),其中 \(n\) 是機器中貨架的數量,\(k\) 是 Arseniy 可用的瓶子數量。

接下來的 \(k\) 行包含兩個整數 \(b_i\)\(c_i\) \((1 \leq b_i \leq k, 1 \leq c_i \leq 1000)\) - 第 \(i\) 瓶的品牌和成本。

我們還保證所有測試用例中 \(n\) 的總和不超過 \(2 \cdot 10^5\),所有測試用例中 \(k\) 的總和也不超過 \(2 \cdot 10^5\)

輸出
對於每個測試用例,輸出一個整數 - Arseniy 可以賺取的最大金額。

示例
輸入

4
3 3
2 6
2 7
1 15
1 3
2 6
2 7
1 15
6 2
1 7
2 5
190000 1
1 1000

輸出

28
15
12
1000

備註
在第一個測試用例中,阿爾謝尼在自動售貨機中有 3 個貨架。他可以在第一個貨架上放置兩個品牌 2 的瓶子,在第二個貨架上放置一個品牌 1 的瓶子。然後瓶子的總成本將為 \(6 + 7 + 15 = 28\)

在第二個測試用例中,他只有一個貨架。很容易證明,最優方案是在上面放置一個品牌 1 的瓶子。然後總成本將為 \(15\)

在第三個測試用例中,他有多達 6 個貨架,因此他可以放置所有可用瓶子,總成本為 \(7 + 5 = 12\)

解題思路

開一個map存一下每種品牌的總價值,然後再開一個優先佇列儲存map裡面的值,最後將前\(\min(n,k)\)相加輸出即可

程式碼實現

void solve()
{
    int n, k;
    cin >> n >> k;
    map<int, int> mp;
    for (int i = 0; i < k; i++)
    {
        int x, y;
        cin >> x >> y;
        mp[x] += y;
    }
    priority_queue<int> q;
    for (auto i : mp)
    {
        q.push(i.sd);
    }
    ll ans = 0;
    int cnt = 0;
    while (!q.empty() && cnt < k && cnt < n)
    {
        ans += q.top();
        q.pop();
        cnt++;
    }
    cout << ans << endl;
}

C. Anya and 1100

在遠處抽屜裡翻找東西時,Anya 發現了一個漂亮的字串 \(s\) ,它只由 0 和 1 組成。

現在,她想透過對其執行 \(q\) 操作讓它變得更加漂亮。

每個操作由兩個整數 \(i\) \((1 \leq i \leq |s|)\)\(v\) \((v \in \{0, 1\})\) 描述,這意味著字串的第 \(i\) 個字元被賦予值 \(v\) (即執行賦值 \(s_i = v\))。

但是 Anya 喜歡數字 1100,因此每次查詢之後,她都會請您告訴她子字串“1100”是否存在於她的字串中(即,存在這樣的 \(1 \leq i \leq |s| - 3\) 使得 \(s_i s_{i+1} s_{i+2} s_{i+3} = 1100\))。

輸入
第一行包含一個整數 \(t\) \((1 \leq t \leq 10^4)\) — 測試用例的數量。

測試用例的第一行包含字串 \(s\) \((1 \leq |s| \leq 2 \cdot 10^5)\),僅由字元“0”和“1”組成。這裡 \(|s|\) 表示字串 \(s\) 的長度。

下一行包含一個整數 \(q\) \((1 \leq q \leq 2 \cdot 10^5)\) — 查詢的數量。

接下來的 \(q\) 行包含兩個整數 \(i\) \((1 \leq i \leq |s|)\)\(v\) \((v \in \{0, 1\})\),描述查詢。

保證所有測試用例的 \(|s|\) 總和不超過 \(2 \cdot 10^5\)。還保證所有測試用例的 \(q\) 總和不超過 \(2 \cdot 10^5\)

輸出
對於每個查詢,如果 Anya 的字串中存在“1100”,則輸出“YES”;否則,輸出“NO”。

您可以以任何大小寫(大寫或小寫)輸出答案。例如,字串“yEs”、“yes”、“Yes”和“YES”將被識別為肯定響應。

示例
輸入

4
100
4
1 1
2 0
2 0
3 1
1100000
3
6 1
7 1
4 1
111010
4
1 1
5 0
4 1
5 0
0100
4
3 1
1 1
2 0
2 1

輸出

NO
NO
NO
NO
YES
YES
NO
NO
YES
YES
YES
NO
NO
NO
NO

解題思路

先統計出有多少個"1100",然後每次修改位置\(i\),暴力查詢第\(i-3\sim i+3\)個位置,看是修改前有多少個"1100",修改後有多少個"1100"即可。

程式碼實現

void solve()
{
    string s;
    cin >> s;
    int n = s.size();
    int cnt = 0;
    for (int i = 0; i <= n - 4; ++i)
    {
        if (s[i] == '1' && s[i + 1] == '1' && s[i + 2] == '0' && s[i + 3] == '0')
            cnt++;
    }
    int q;
    cin >> q;
    while (q--)
    {
        int pos, v;
        cin >> pos >> v;
        pos--;
        int start = max(0, pos - 3);
        int end = min(n - 4, pos);
        for (int i = start; i <= end; ++i)
        {
            if (s[i] == '1' && s[i + 1] == '1' && s[i + 2] == '0' && s[i + 3] == '0')
                cnt--;
        }
        s[pos] = (v == 0) ? '0' : '1';
        for (int i = start; i <= end; ++i)
        {
            if (s[i] == '1' && s[i + 1] == '1' && s[i + 2] == '0' && s[i + 3] == '0')
                cnt++;
        }
        if (cnt > 0)
            YES;
        else
            NO;
    }
}

D. I Love 1543

一天早上,波利卡普醒來後發現,1543 是他一生中最喜歡的數字。

波利卡普那天一睜開眼睛,看到的第一件事就是一張巨大的壁毯,大小為 \(n\)\(m\) 列;\(n\)\(m\) 是偶數。每個格子包含從 0 到 9 的數字之一。

波利卡普很好奇,當順時針方向移動時,數字 1543 會在地毯的所有層中出現多少次。

  • 尺寸為 \(n \times m\) 的地毯的第一層定義為長度為 \(2 \cdot (n + m - 2)\) 且厚度為 1 元素的封閉條帶,環繞其外部。每個後續層都定義為地毯的第一層,透過從原始地毯中移除所有先前的層而獲得。

輸入
輸入的第一行包含一個整數 \(t\) \((1 \leq t \leq 100)\) — 測試用例的數量。以下幾行描述了測試用例。

每個測試用例的第一行包含一對數字 \(n\)\(m\) \((2 \leq n, m \leq 10^3, n, m\) — 偶數)。

接下來是 \(n\) 行,長度為 \(m\) ,由從 0 到 9 的數字組成 — 地毯的描述。

保證所有測試用例的 \(n \cdot m\) 的總和不超過 \(10^6\)

輸出
對於每個測試用例,輸出一個數字—— 1543 按照順時針遍歷順序出現在所有地毯層中的總次數。

示例
輸入

8
2 4
1543
7777
2 4
7154
8903
2 4
3451
8888
2 2
54
13
2 2
51
43
2 6
432015
512034
4 4
5431
1435
5518
7634
6 4
5432
1152
4542
2432
2302
5942

輸出

1
1
0
1
0
2
2
2

備註

image-20241103032910082

第七個示例中 1543 的出現次數。不同的層用不同的顏色標記。

解題思路

計算出每一層的邊界,然後迴圈列舉每一層,將數字加入字串中,最後遍歷字串,統計有多少個1534即可。

為了處理迴圈,記得要在字串結尾要多插入3個數字。

時間複雜度為\(O(nm)\)

程式碼實現

void solve()
{
    int n, m;
    cin >> n >> m;
    vs grid(n);
    for (int i = 0; i < n; ++i)
    {
        cin >> grid[i];
    }
    int len = min(n, m) / 2;
    int ans = 0;
    for (int i = 0; i < len; i++)
    {
        int U = i;
        int D = n - i - 1;
        int L = i;
        int R = m - i - 1;
        string now = "";
        for (int j = L; j <= R; j++)
        {
            now += grid[U][j];
        }
        for (int j = U + 1; j <= D; j++)
        {
            now += grid[j][R];
        }
        if (D != U)
        {
            for (int j = R - 1; j >= L; j--)
            {
                now += grid[D][j];
            }
        }
        if (L != R)
        {
            for (int j = D - 1; j >= U + 1; j--)
            {
                now += grid[j][L];
            }
        }
        string temp = now;
        if (now.size() >= 3)
            temp += now.substr(0, 3);
        else
            continue;
        for (int j = 0; j + 4 <= temp.size(); j++)
        {
            if (temp.substr(j, 4) == "1543")
                ans++;
        }
    }
    cout

E. Reverse the Rivers

古代聖賢為了自己的方便,決定改變河流的流向,他們的陰謀將世界置於危險的邊緣。但在實施他們的宏偉計劃之前,他們決定仔細考慮一下他們的戰略——這就是聖人的做法。

世界上有 \(n\) 個國家,每個國家正好有 \(k\) 個地區。對於第 \(i\) 個國家的第 \(j\) 個地區,他們計算出了 \(a_{i,j}\) 這個值,它反映了其中的水量。

聖人打算在第 \(i\) 個國家的第 \(j\) 個區域和第 \((i+1)\) 個國家的第 \(j\) 個區域之間為所有的 \(1 \leq i \leq (n-1)\) 和所有的 \(1 \leq j \leq k\) 修建水渠。

由於所有 \(n\) 個國家都在一個大斜坡上,所以水會流向數字最高的國家。根據聖人的預測,在通道系統建立之後,第 \(i\) 個國家的第 \(j\) 個區域的新值將是

\[b_{i,j} = a_{1,j} | a_{2,j} | \ldots | a_{i,j} \]

其中 \(|\) 表示位元 "OR" 運算。

在重新分配水源之後,聖人的目的是選擇最適合居住的國家,因此他們會向你提出 \(q\) 個問題供你考慮。

每個問題都包含 \(m\) 項要求。

每個要求包含三個引數:地區編號 \(r\)、符號 \(o\)(" < " 或 " > ")和值 \(c\)。如果 \(o = " < "\),那麼在所選國家的第 \(r\) 個區域內,新值必須嚴格小於限值 \(c\),如果 \(o = " > "\),則必須嚴格大於限值。

換句話說,所選國家 \(i\) 必須滿足所有 \(m\) 要求。如果當前要求中 \(o = " < "\),那麼 \(b_{i,r} < c\) 必須成立;如果 \(o = " > "\),那麼 \(b_{i,r} > c\) 也必須成立。

在回答每個查詢時,您應該輸出一個整數——合適國家的編號。如果有多個這樣的國家,則輸出最小的一個。如果不存在這樣的國家,則輸出 \(-1\)

輸入
第一行包含三個整數 \(n\)\(k\)\(q\) \((1 \leq n, k, q \leq 10^5)\) — 分別是國家、地區和查詢的數量。

接下來是 \(n\) 行,其中第 \(i\) 行包含 \(k\) 個整數 \(a_{i,1}, a_{i,2}, \ldots, a_{i,k}\) \((1 \leq a_{i,j} \leq 10^9)\),其中 \(a_{i,j}\) 是第 \(i\) 個國家的第 \(j\) 個地區的值。

然後,\(q\) 個查詢被描述出來。

每個查詢的第一行包含一個整數 \(m\) \((1 \leq m \leq 10^5)\) — 需求的數量。

然後是 \(m\) 行,每行包含一個整數 \(r\)、一個字元 \(o\) 和一個整數 \(c\) \((1 \leq r \leq k, 0 \leq c \leq 2 \cdot 10^9)\),其中 \(r\)\(c\) 是區域編號和數值,\(o\) 是 " < " 或 " > " — 符號。

可以保證 \(n \cdot k\) 不超過 \(10^5\),所有查詢的 \(m\) 之和也不超過 \(10^5\)

輸出
對於每個查詢,在新行中輸出一個整數——合適國家的最小編號,如果不存在這樣的國家,則輸出 \(-1\)

示例
輸入

3 4 4
1 3 5 9
4 6 5 3
2 1 2 7
3
1 > 4
2 < 8
1 < 6
2
1 < 8
2 > 8
1
3 > 5
2
4 > 8
1 < 8

輸出

2
-1
3
1

備註
在示例中,區域的初始值如下:

\(1\) \(3\) \(5\) \(9\)
\(4\) \(6\) \(5\) \(3\)
\(2\) \(1\) \(2\) \(7\)

建立通道後,新值將如下所示:

\(1\) \(3\) \(5\) \(9\)
\(1 | 4\) \(3 | 6\) \(5 | 5\) \(9 | 3\)
\(1 | 4 | 2\) \(3 | 6 | 1\) \(5 | 5 | 2\) \(9 | 3 | 7\)
--- --- --- ---
\(5\) \(7\) \(5\) \(11\)
\(7\) \(7\) \(7\) \(15\)

在第一個查詢中,需要輸出最小國家編號(即行),在重新分配水後,第一個區域(即列)的新值大於 \(4\) 且小於 \(6\),而第二個區域小於 \(8\)。只有編號為 \(2\) 的國家滿足這些要求。

在第二個查詢中,沒有國家滿足指定的要求。

在第三個查詢中,只有編號為 \(3\) 的國家是合適的。

在第四個查詢中,所有三個國家都滿足條件,因此答案是最小的編號 \(1\)

解題思路

由於按位或是單調不減的,因此我們可以預處理出每個區域的字首按位或和,其一定是一個單調不減序列。

我們對這個序列進行二分查詢即可。

程式碼實現

void solve()
{
    int n, k, q;
    cin >> n >> k >> q;
    vvi b(k, vi(n));
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < k; j++)
        {
            cin >> b[j][i];
        }
    }
    for (int j = 0; j < k; j++)
    {
        for (int i = 1; i < n; i++)
        {
            b[j][i] = b[j][i - 1] | b[j][i];
        }
    }
    while (q--)
    {
        int m;
        cin >> m;
        int mn = 1;
        int mx = n;
        for (int i = 0; i < m; i++)
        {
            int r;
            char o;
            int c;
            cin >> r >> o >> c;
            int p = r - 1;
            if (o == '<')
            {
                auto &v = b[p];
                int pos = lower_bound(all(v), c) - v.begin();
                mx = min(mx, pos);
            }
            else if (o == '>')
            {
                auto &v = b[p];
                int pos = upper_bound(all(v), c) - v.begin();
                mn = max(mn, pos + 1);
            }
        }
        if (mn <= mx && mn <= n && mx >= 1)
            cout << mn << "\n";
        else
            cout << "-1\n";
    }
}

F. XORificator 3000

愛麗絲送鮑勃禮物已經很多年了,她知道鮑勃最喜歡的是對有趣的整數進行 bitwise XOR。鮑勃認為,如果正整數 $ x $ 滿足 $ x \not\equiv k \mod 2^i $,那麼這個正整數就是有趣的。因此,在今年鮑勃生日時,她送給他一臺超級強大的最新型號 "XORificator 3000"。

鮑勃對這份禮物非常滿意,因為它能讓他立即計算出從 $ l $ 到 $ r $ (含)任意範圍內所有有趣整數的 XOR。畢竟,一個人的幸福還需要什麼呢?不幸的是,這個裝置太強大了,有一次,它與自己進行了 XOR 運算,然後就消失了。鮑勃非常沮喪,為了讓他開心起來,愛麗絲讓你寫出你版本的 "XORificator"。

輸入
第一行輸入包含一個整數 $ t $ \((1 \leq t \leq 10^4)\) — 段上的 XOR 查詢次數。接下來的 $ t $ 行包含查詢,每個查詢由整數 $ l \(、\) r \(、\) i \(、\) k $ 組成 \((1 \leq l \leq r \leq 10^{18}, 0 \leq i \leq 30, 0 \leq k < 2^i)\)

輸出
對於每個查詢,輸出一個整數 — 範圍為 \([l,r]\) 的所有整數 $ x $ 的 XOR,即 $ x \not\equiv k \mod 2^i $。

示例
輸入

6
1 3 1 0
2 28 3 7
15 43 1 0
57 2007 1 0
1010 1993 2 2
1 1000000000 30 1543

輸出

2
2
13
0
4
1000000519

備註
在第一個查詢中,範圍 \([1,3]\) 中的有趣整數是 \(1\)\(3\),因此答案為 \(1 \oplus 3 = 2\)

解題思路

如果你手玩過連續異或和,你會發現一個非常有趣的性質,連續四個自然數異或和為\(0\)

例如\(0\oplus 1 \oplus 2\oplus3=0\)\(12\oplus13\oplus14\oplus15=0\)。這是因為這四個數最後兩位都是\(00_2,01_2,10_2,11_2\),除這兩個數外,其它數位的\(1\)的數量都為偶數。

這是一個在二進位制類題目中非常常用的性質。

利用這個性質,我們可以快速計算出\(1\sim n\)的字首異或和\(f(n)\)

\[f(n) = \begin{cases} n & \ n \mod 4 = 0 \\ 1 & \ n \mod 4 = 1 \\ n + 1 & \ n \mod 4 = 2 \\ 0 & \ n \mod 4 = 3 \\ \end{cases} \]

利用異或和的交換性,我們可以快速計算出\(l\sim r\)的區間異或\(\text{xsum}=f(r)\oplus f(l-1)\)

有了區間異或和之後,我們就只需要計算出區間不有趣整數的異或和就可以得到區間有趣整數的異或和。

觀察對\(2^i\)同餘的數的二進位制位。例如\(x\equiv 2^4 (\mod 3)\)

\(000011_2,010011_2,100011_2,110011_2,\dots\)

可以發現後\(i\)位對異或和沒有有影響,只有前幾位有影響

因此區間不有趣整數的字首異或和\(g(n,i,k)\)計算方式如下

\[g(n, i, k) = \text{f}(m) \times 2^i \oplus k[\text{cnt}_x\mod2=1] \]

其中\(m= \left\lfloor \frac{n - k}{2^i} \right\rfloor\)\(\text{cnt}_x = m+ 1\)\(\text{cnt}_x\)即為區間不有趣整數的數量

程式碼實現

ull f(ull n)
{
    if (n % 4 == 0)
        return n;
    else if (n % 4 == 1)
        return 1;
    else if (n % 4 == 2)
        return n + 1;
    else
        return 0;
}

ull g(ull n, ull i, ull k)
{
    if (i == 0)
    {
        if (k == 0)
            return f(n);
        else
            return 0; // 當i=0且k≠0時,無滿足條件的x
    }
    ull pow2 = 1ULL << i;
    // 如果n小於k,則沒有滿足條件的x
    if (n < k)
        return 0;
    ull m = (n - k) / pow2;
    ull cnt = m + 1;
    ull res = f(m) << i;
    if (cnt % 2 == 1)
        res ^= k;
    return res;
}

void solve()
{
    ull l, r, k;
    int i;
    cin >> l >> r >> i >> k;
    ull sum = f(r) ^ f(l - 1);
    ull Xor = g(r, i, k) ^ g(l - 1, i, k);
    ull ans = sum ^ Xor;
    cout << ans << "\n";
}

這場感覺挺簡單的,跟div4的難度差不多,早知道打小號上分了、

本來想著一邊寫題看比賽,一邊寫題,可能打的不咋地,乾脆打大號不計分。哎,還不如專心打cf。

G 最開始想的是二分加三分,先二分出最大和最小的書,然後再對中間的書進行三分,算了一下次數,剛好差不多。但是卻沒考慮到\(a\oplus b\oplus c=0\)的情況。具體的思路我已經會了,但實現起來很麻範,等我有空補一補吧。

相關文章