P7442 「EZEC-7」维护序列 题解

题目传送门

题面

题目背景

](https://www.luogu.com.cn/paste/tdqr0sto)

可怜的 dead_X 收不了歌,于是他出了个水题并给参赛者送了 100 100 100 分。

2022 Update: 已经收了,很水。

题目描述

你需要维护一个序列。

这个序列开始时有 2 n 2^n 2n 个数,下标从 0 0 0 开始。第 i i i 个数初始值为 i i i,需要支持以下三种操作:

  • 定义 a a a 为所有下标为偶数的数组成的子序列, b b b 为所有下标为奇数的数组成的子序列,将 a , b a,b a,b 连接,构成新的序列。
  • 定义 a a a 为所有下标为奇数的数组成的子序列, b b b 为所有下标为偶数的数组成的子序列,将 a , b a,b a,b 连接,构成新的序列。
  • 查询下标为 x x x 的数。

总共将进行 m m m 次操作。

输入格式

第一行输入两个正整数 n , m n,m n,m

接下来输入 m m m 行,每行输入两个非负整数 o p , x op,x op,x,代表一次操作。

如果 o p = 1 op=1 op=1,若 x = 0 x=0 x=0,代表第一种操作,若 x = 1 x=1 x=1,代表第二种操作。

如果 o p = 2 op=2 op=2,代表第三种操作,参数 x x x 即为输入的 x x x

输出格式

对于每个 o p = 2 op=2 op=2 输出一行,即对应的数。

输入输出样例 #1

输入 #1

2 7
2 0
1 0
2 1
1 1
2 2
1 0
2 3

输出 #1

0
2
0
1

说明/提示

【样例解释】

所有操作前后的序列从左至右的数如下:
{ 0 , 1 , 2 , 3 } \{0,1,2,3\} {0,1,2,3}
下标为 0 0 0 的数为 0 0 0
{ 0 , 2 } , { 1 , 3 } \{0,2\},\{1,3\} {0,2},{1,3}
{ 0 , 2 , 1 , 3 } \{0,2,1,3\} {0,2,1,3}
下标为 1 1 1 的数为 2 2 2
{ 2 , 3 } , { 0 , 1 } \{2,3\},\{0,1\} {2,3},{0,1}
{ 2 , 3 , 0 , 1 } \{2,3,0,1\} {2,3,0,1}
下标为 2 2 2 的数为 0 0 0
{ 2 , 0 } , { 3 , 1 } \{2,0\},\{3,1\} {2,0},{3,1}
{ 2 , 0 , 3 , 1 } \{2,0,3,1\} {2,0,3,1}
下标为 3 3 3 的数为 1 1 1

【数据范围】

本题采用捆绑测试。

  • Subtask 1(10 points):不存在 o p = 1 op=1 op=1 的操作。
  • Subtask 2(10 points): n ≤ 10 , m ≤ 1 0 3 n\leq 10,m\leq 10^3 n10m103
  • Subtask 3(20 points): n ≤ 10 n\leq 10 n10
  • Subtask 4(20 points): m ≤ 1 0 3 m\leq 10^3 m103
  • Subtask 5(20 points):对于 o p = 1 op=1 op=1 的操作, x = 0 x=0 x=0
  • Subtask 6(20 points):无特殊限制。

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 32 1\leq n\leq 32 1n32 1 ≤ m ≤ 1 0 6 1\leq m\leq 10^6 1m106

o p = 1 op=1 op=1 x ∈ { 0 , 1 } x\in\{0,1\} x{0,1},若 o p = 2 op=2 op=2 0 ≤ x < 2 n 0\leq x<2^n 0x<2n

题目解析

给你一个长度为 2 n 2^n 2n 的序列,下标从 0 0 0 开始,初始时每个元素的值等于他的下标。执行以下操作:

  1. 将下标为奇数的元素子序列接到下标为偶数的元素后。
  2. 将下标为偶数的元素子序列接到下标为奇数的元素后。
  3. 求下标 x x x 的元素的值。

思路

标签:位运算。

我们遇见这种显然不能暴力的题目,要先把样例画出来,或者自己找一下规律。

n = 2 n = 2 n=2 为例,原式为 { 0 , 1 , 2 , 3 } \{ 0,1,2,3 \} {0,1,2,3}

执行操作 1 1 1 后,原式为 { 0 , 2 , 1 , 3 } \{ 0,2,1,3 \} {0,2,1,3}

执行操作 2 2 2 后,原式为 { 1 , 3 , 0 , 2 } \{ 1,3,0,2 \} {1,3,0,2}

执行操作 1 1 1 再执行操作 2 2 2 后,原式为 { 2 , 3 , 0 , 1 } \{ 2,3,0,1 \} {2,3,0,1}

把这四个式子转成二进制:

  • 00 , 01 , 10 , 11 00,01,10,11 00,01,10,11
  • 00 , 10 , 01 , 11 00,10,01,11 00,10,01,11
  • 01 , 11 , 00 , 10 01,11,00,10 01,11,00,10
  • 10 , 11 , 00 , 01 10,11,00,01 10,11,00,01

发现什么没有?操作 1 1 1 相当于把二进制的最高位转到最低位,操作 2 2 2 则是在操作 1 1 1 的基础上异或 1 1 1 。你可以试试再打一下 n = 3 n = 3 n=3 的情况,这里不再赘述。

所以我们记录当前转了 x x x 次和结果要异或 y y y。如果 x = n x = n x=n 次就归零。我们可以看见第四个式子其实是异或了 2 2 2 的,这是为什么呢?转了一次之后,原本 1 1 1 的位置就会左移,所以我们对于每个 2 2 2 操作要做 y ← y ⊕ 2 x y \leftarrow y \oplus 2^x yy2x ,再把 x x x 加上一。

最后的输出怎么处理呢?原本下标 x x x 处的值就是 x x x ,然后原本的数字我们先右移 n − x n - x nx 位,表示将前 x x x 位移动到末尾,然后把原数与 2 n − x − 1 2 ^ {n - x} - 1 2nx1 取按位与得到没有被移到末尾的数,把他们左移 x x x 位。最后把两个值取按位或,在与 y y y 取按位异或即可。注意特判 x = 0 x = 0 x=0 的情况,这时只需将原数与 y y y 按位异或即可。

公式就是 ( x ≫ ( n − x ) ) ∣ ( ( x & ( ( 1 ≪ ( n − x ) ) − 1 ) ) ≪ x ) ⊕ y (x \gg (n - x)) | ((x \& ((1 \ll (n - x)) - 1)) \ll x) \oplus y (x(nx))((x&((1(nx))1))x)y

CODE

#include<iostream>
#define int long long
using namespace std;
int n,m;
int turn,xors;
signed main()
{
    cin >> n >> m;
    while(m--)
    {
        int op,x;
        cin >> op >> x;
        if(op == 1)
        {
            if(x) xors ^= (1ll << turn);
            turn++;
            turn %= n;
        }
        else
        {
            if(turn)
            {
                cout << (((x >> (n - turn)) | ((x & ((1ll << (n - turn)) - 1)) << turn)) ^ xors);               
            }
            else cout << (x ^ xors);
            cout << '\n';
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值