Codeforces Round 984 div3 个人题解(A~F)

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 a a b b b 之间的间隔等于 ∣ a − b ∣ |a−b| ab 个半音。

如果每两个相邻音符之间的音程是 5 个半音或 7 个半音,Boris 就会认为旋律是完美的。

在创作完最新的旋律后,他热情地向您展示了他的作品集。帮助鲍里斯-诺特金了解他的旋律是否完美。

输入
第一行包含一个整数 t t t ( 1 ≤ t ≤ 1000 ) (1 \leq t \leq 1000) (1t1000) - 旋律的数量。

每首旋律由两行描述。

第一行包含一个整数 n n n ( 2 ≤ n ≤ 50 ) (2 \leq n \leq 50) (2n50) --旋律中的音符数。

第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an ( 0 ≤ a i ≤ 127 ) (0 \leq a_i \leq 127) (0ai127) --旋律的音符。

输出
对于每段旋律,如果完美,则输出 “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 a 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 n n 个货架和 k k k 个瓶子的自动售货机,其中第 i i i 个瓶子的品牌指数为 b i b_i bi,成本为 c i c_i ci

您可以在每个货架上放置任意数量的瓶子,但同一货架上的所有瓶子必须是同一品牌。

Arseniy 知道他放在机器货架上的所有瓶子都会卖掉。因此,他要求您计算出他能赚取的最大金额。

输入
第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 ) (1 \leq t \leq 10^4) (1t104) - 测试用例数。

每个测试用例的第一行包含两个整数 n n n k k k ( 1 ≤ n , k ≤ 2 ⋅ 1 0 5 ) (1 \leq n, k \leq 2 \cdot 10^5) (1n,k2105),其中 n n n 是机器中货架的数量, k k k 是 Arseniy 可用的瓶子数量。

接下来的 k k k 行包含两个整数 b i b_i bi c i c_i ci ( 1 ≤ b i ≤ k , 1 ≤ c i ≤ 1000 ) (1 \leq b_i \leq k, 1 \leq c_i \leq 1000) (1bik,1ci1000) - 第 i i i 瓶的品牌和成本。

我们还保证所有测试用例中 n n n 的总和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105,所有测试用例中 k k k 的总和也不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出
对于每个测试用例,输出一个整数 - 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 6 + 7 + 15 = 28 6+7+15=28

在第二个测试用例中,他只有一个货架。很容易证明,最优方案是在上面放置一个品牌 1 的瓶子。然后总成本将为 15 15 15

在第三个测试用例中,他有多达 6 个货架,因此他可以放置所有可用瓶子,总成本为 7 + 5 = 12 7 + 5 = 12 7+5=12

解题思路

开一个map存一下每种品牌的总价值,然后再开一个优先队列存储map里面的值,最后将前 min ⁡ ( n , k ) \min(n,k) 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 s s ,它只由 0 和 1 组成。

现在,她想通过对其执行 q q q 操作让它变得更加漂亮。

每个操作由两个整数 i i i ( 1 ≤ i ≤ ∣ s ∣ ) (1 \leq i \leq |s|) (1is) v v v ( v ∈ { 0 , 1 } ) (v \in \{0, 1\}) (v{0,1}) 描述,这意味着字符串的第 i i i 个字符被赋予值 v v v (即执行赋值 s i = v s_i = v si=v)。

但是 Anya 喜欢数字 1100,因此每次查询之后,她都会请您告诉她子字符串“1100”是否存在于她的字符串中(即,存在这样的 1 ≤ i ≤ ∣ s ∣ − 3 1 \leq i \leq |s| - 3 1is3 使得 s i s i + 1 s i + 2 s i + 3 = 1100 s_i s_{i+1} s_{i+2} s_{i+3} = 1100 sisi+1si+2si+3=1100)。

输入
第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 ) (1 \leq t \leq 10^4) (1t104) — 测试用例的数量。

测试用例的第一行包含字符串 s s s ( 1 ≤ ∣ s ∣ ≤ 2 ⋅ 1 0 5 ) (1 \leq |s| \leq 2 \cdot 10^5) (1s2105),仅由字符“0”和“1”组成。这里 ∣ s ∣ |s| s 表示字符串 s s s 的长度。

下一行包含一个整数 q q q ( 1 ≤ q ≤ 2 ⋅ 1 0 5 ) (1 \leq q \leq 2 \cdot 10^5) (1q2105) — 查询的数量。

接下来的 q q q 行包含两个整数 i i i ( 1 ≤ i ≤ ∣ s ∣ ) (1 \leq i \leq |s|) (1is) v v v ( v ∈ { 0 , 1 } ) (v \in \{0, 1\}) (v{0,1}),描述查询。

保证所有测试用例的 ∣ s ∣ |s| s 总和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105。还保证所有测试用例的 q q q 总和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出
对于每个查询,如果 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 i,暴力查询第 i − 3 ∼ i + 3 i-3\sim i+3 i3i+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 n n m m m 列; n n n m m m 是偶数。每个格子包含从 0 到 9 的数字之一。

波利卡普很好奇,当顺时针方向移动时,数字 1543 会在地毯的所有层中出现多少次。

  • 尺寸为 n × m n \times m n×m 的地毯的第一层定义为长度为 2 ⋅ ( n + m − 2 ) 2 \cdot (n + m - 2) 2(n+m2) 且厚度为 1 元素的封闭条带,环绕其外部。每个后续层都定义为地毯的第一层,通过从原始地毯中移除所有先前的层而获得。

输入
输入的第一行包含一个整数 t t t ( 1 ≤ t ≤ 100 ) (1 \leq t \leq 100) (1t100) — 测试用例的数量。以下几行描述了测试用例。

每个测试用例的第一行包含一对数字 n n n m m m ( 2 ≤ n , m ≤ 1 0 3 , n , m (2 \leq n, m \leq 10^3, n, m (2n,m103,n,m — 偶数)。

接下来是 n n n 行,长度为 m m m ,由从 0 到 9 的数字组成 — 地毯的描述。

保证所有测试用例的 n ⋅ m n \cdot m nm 的总和不超过 1 0 6 10^6 106

输出
对于每个测试用例,输出一个数字—— 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

备注

在这里插入图片描述

第七个示例中 1543 的出现次数。不同的层用不同的颜色标记。

解题思路

计算出每一层的边界,然后循环枚举每一层,将数字加入字符串中,最后遍历字符串,统计有多少个1534即可。

为了处理循环,记得要在字符串结尾要多插入3个数字。

时间复杂度为 O ( n m ) O(nm) 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 n n 个国家,每个国家正好有 k k k 个地区。对于第 i i i 个国家的第 j j j 个地区,他们计算出了 a i , j a_{i,j} ai,j 这个值,它反映了其中的水量。

圣人打算在第 i i i 个国家的第 j j j 个区域和第 ( i + 1 ) (i+1) (i+1) 个国家的第 j j j 个区域之间为所有的 1 ≤ i ≤ ( n − 1 ) 1 \leq i \leq (n-1) 1i(n1) 和所有的 1 ≤ j ≤ k 1 \leq j \leq k 1jk 修建水渠。

由于所有 n n n 个国家都在一个大斜坡上,所以水会流向数字最高的国家。根据圣人的预测,在通道系统建立之后,第 i i i 个国家的第 j j j 个区域的新值将是

b i , j = a 1 , j ∣ a 2 , j ∣ … ∣ a i , j b_{i,j} = a_{1,j} | a_{2,j} | \ldots | a_{i,j} bi,j=a1,ja2,jai,j

其中 ∣ | 表示比特 “OR” 运算。

在重新分配水源之后,圣人的目的是选择最适合居住的国家,因此他们会向你提出 q q q 个问题供你考虑。

每个问题都包含 m m m 项要求。

每个要求包含三个参数:地区编号 r r r、符号 o o o(" < " 或 " > ")和值 c c c。如果 o = " < " o = " < " o="<",那么在所选国家的第 r r r 个区域内,新值必须严格小于限值 c c c,如果 o = " > " o = " > " o=">",则必须严格大于限值。

换句话说,所选国家 i i i 必须满足所有 m m m 要求。如果当前要求中 o = " < " o = " < " o="<",那么 b i , r < c b_{i,r} < c bi,r<c 必须成立;如果 o = " > " o = " > " o=">",那么 b i , r > c b_{i,r} > c bi,r>c 也必须成立。

在回答每个查询时,您应该输出一个整数——合适国家的编号。如果有多个这样的国家,则输出最小的一个。如果不存在这样的国家,则输出 − 1 -1 1

输入
第一行包含三个整数 n n n k k k q q q ( 1 ≤ n , k , q ≤ 1 0 5 ) (1 \leq n, k, q \leq 10^5) (1n,k,q105) — 分别是国家、地区和查询的数量。

接下来是 n n n 行,其中第 i i i 行包含 k k k 个整数 a i , 1 , a i , 2 , … , a i , k a_{i,1}, a_{i,2}, \ldots, a_{i,k} ai,1,ai,2,,ai,k ( 1 ≤ a i , j ≤ 1 0 9 ) (1 \leq a_{i,j} \leq 10^9) (1ai,j109),其中 a i , j a_{i,j} ai,j 是第 i i i 个国家的第 j j j 个地区的值。

然后, q q q 个查询被描述出来。

每个查询的第一行包含一个整数 m m m ( 1 ≤ m ≤ 1 0 5 ) (1 \leq m \leq 10^5) (1m105) — 需求的数量。

然后是 m m m 行,每行包含一个整数 r r r、一个字符 o o o 和一个整数 c c c ( 1 ≤ r ≤ k , 0 ≤ c ≤ 2 ⋅ 1 0 9 ) (1 \leq r \leq k, 0 \leq c \leq 2 \cdot 10^9) (1rk,0c2109),其中 r r r c c c 是区域编号和数值, o o o 是 " < " 或 " > " — 符号。

可以保证 n ⋅ k n \cdot k nk 不超过 1 0 5 10^5 105,所有查询的 m m m 之和也不超过 1 0 5 10^5 105

输出
对于每个查询,在新行中输出一个整数——合适国家的最小编号,如果不存在这样的国家,则输出 − 1 -1 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 1 1 3 3 3 5 5 5 9 9 9
4 4 4 6 6 6 5 5 5 3 3 3
2 2 2 1 1 1 2 2 2 7 7 7

创建通道后,新值将如下所示:

1 1 1 3 3 3 5 5 5 9 9 9
$14$$36$
$142$$3
5 5 5 7 7 7 5 5 5 11 11 11
7 7 7 7 7 7 7 7 7 15 15 15

在第一个查询中,需要输出最小国家编号(即行),在重新分配水后,第一个区域(即列)的新值大于 4 4 4 且小于 6 6 6,而第二个区域小于 8 8 8。只有编号为 2 2 2 的国家满足这些要求。

在第二个查询中,没有国家满足指定的要求。

在第三个查询中,只有编号为 3 3 3 的国家是合适的。

在第四个查询中,所有三个国家都满足条件,因此答案是最小的编号 1 1 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 ≤ t ≤ 1 0 4 ) (1 \leq t \leq 10^4) (1t104) — 段上的 XOR 查询次数。接下来的 $ t $ 行包含查询,每个查询由整数 $ l 、 、 r 、 、 i 、 、 k $ 组成 ( 1 ≤ l ≤ r ≤ 1 0 18 , 0 ≤ i ≤ 30 , 0 ≤ k < 2 i ) (1 \leq l \leq r \leq 10^{18}, 0 \leq i \leq 30, 0 \leq k < 2^i) (1lr1018,0i30,0k<2i)

输出
对于每个查询,输出一个整数 — 范围为 [ l , r ] [l,r] [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,3] 中的有趣整数是 1 1 1 3 3 3,因此答案为 1 ⊕ 3 = 2 1 \oplus 3 = 2 13=2

解题思路

如果你手玩过连续异或和,你会发现一个非常有趣的性质,连续四个自然数异或和为 0 0 0

例如 0 ⊕ 1 ⊕ 2 ⊕ 3 = 0 0\oplus 1 \oplus 2\oplus3=0 0123=0 12 ⊕ 13 ⊕ 14 ⊕ 15 = 0 12\oplus13\oplus14\oplus15=0 12131415=0。这是因为这四个数最后两位都是 0 0 2 , 0 1 2 , 1 0 2 , 1 1 2 00_2,01_2,10_2,11_2 002,012,102,112,除这两个数外,其它数位的 1 1 1的数量都为偶数。

这是一个在二进制类题目中非常常用的性质。

利用这个性质,我们可以快速计算出 1 ∼ n 1\sim n 1n的前缀异或和 f ( n ) f(n) f(n)
f ( n ) = { n   n m o d    4 = 0 1   n m o d    4 = 1 n + 1   n m o d    4 = 2 0   n m o d    4 = 3 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} f(n)= n1n+10 nmod4=0 nmod4=1 nmod4=2 nmod4=3
利用异或和的交换性,我们可以快速计算出 l ∼ r l\sim r lr的区间异或 xsum = f ( r ) ⊕ f ( l − 1 ) \text{xsum}=f(r)\oplus f(l-1) xsum=f(r)f(l1)

有了区间异或和之后,我们就只需要计算出区间不有趣整数的异或和就可以得到区间有趣整数的异或和。

观察对 2 i 2^i 2i同余的数的二进制位。例如 x ≡ 2 4 ( m o d    3 ) x\equiv 2^4 (\mod 3) x24(mod3)

00001 1 2 , 01001 1 2 , 10001 1 2 , 11001 1 2 , … 000011_2,010011_2,100011_2,110011_2,\dots 0000112,0100112,1000112,1100112,

可以发现后 i i i位对异或和没有有影响,只有前几位有影响

因此区间不有趣整数的前缀异或和 g ( n , i , k ) g(n,i,k) g(n,i,k)计算方式如下
g ( n , i , k ) = f ( m ) × 2 i ⊕ k [ cnt x m o d    2 = 1 ] g(n, i, k) = \text{f}(m) \times 2^i \oplus k[\text{cnt}_x\mod2=1] g(n,i,k)=f(m)×2ik[cntxmod2=1]
其中 m = ⌊ n − k 2 i ⌋ m= \left\lfloor \frac{n - k}{2^i} \right\rfloor m=2ink cnt x = m + 1 \text{cnt}_x = m+ 1 cntx=m+1 cnt x \text{cnt}_x cntx即为区间不有趣整数的数量

代码实现
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 ⊕ b ⊕ c = 0 a\oplus b\oplus c=0 abc=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® ^ 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$的情况。具体的思路我已经会了,但实现起来很麻烦,等我有空补一补吧。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值