【训练题63:区间位操作】Kuriyama Mirai and Exclusive Or | 2021牛客暑期多校训练营3

题意

  • Kuriyama Mirai and Exclusive Or | 2021牛客暑期多校训练营3
    给你一个长度为 n n n 的序列 A [ n ] A[n] A[n]
    给你 q q q 次操作,有如下操作:
    [ L , R ] [L,R] [L,R] 内的数字都异或上 x x x
    [ L , R ] [L,R] [L,R] 内的数字,第 i i i 个数字异或上 ( x + i − L ) (x+i-L) (x+iL)
    问你 q q q 次操作完之后,每个位置的值
  • 1 ≤ n ≤ 6 ⋅ 1 0 5 1\le n\le 6\cdot 10^5 1n6105
    1 ≤ q ≤ 4 ⋅ 1 0 5 1\le q\le 4\cdot 10^5 1q4105
    0 ≤ A i < 2 30 0\le A_i<2^{30} 0Ai<230

思路

  • 这题真的太神仙了,难搞懂的一匹…
    由于我们最后输出操作序列就可以了,对于第一种操作,我们只要记一个异或前缀和 b [ n ] b[n] b[n] 就可以了
    也就是我们只需要修改 b [ L ] = b [ L ] ⊕ x b[L]=b[L]\oplus x b[L]=b[L]x b [ R + 1 ] = b [ R + 1 ] ⊕ x b[R+1]=b[R+1]\oplus x b[R+1]=b[R+1]x 即可,这个很好懂
  • 对于第二种操作,我们设 l o w b i t ( x ) = 2 k lowbit(x)=2^k lowbit(x)=2k
    假设 i − l < 2 k i-l<2^k il<2k ,那么我们有:
    a ⊕ ( x + i − L ) = a ⊕ ( x ∣ ( i − L ) ) = a ⊕ x ⊕ ( i − L ) a\oplus (x+i-L)=a\oplus (x|(i-L))=a\oplus x\oplus (i-L) a(x+iL)=a(x(iL))=ax(iL)
    对于左边的 ⊕ x \oplus x x ,就等价于第一种操作
    对于后面的,我们记一个标记 m a r k [ i ] [ j ] mark[i][j] mark[i][j] 就表示:
    a i ⊕ 0 a i + 1 ⊕ 1 a i + 2 ⊕ 2 ⋮ a i + 2 k − 1 ⊕ 2 k − 1 a_i\oplus 0\\ a_{i+1}\oplus 1\\ a_{i+2}\oplus 2\\ \vdots\\ a_{i+{2^k}-1}\oplus 2^k-1\\ ai0ai+11ai+22ai+2k12k1
    这样操作完之后,相当于我们处理完了区间 [ L , L + 2 k − 1 ] [L,L+2^k-1] [L,L+2k1]
    我们让 L = L + 2 k , x = x + 2 k L=L+2^k,x=x+2^k L=L+2kx=x+2k ,然后继续处理
    就是区间左端点下标增加; x x x 增加了是因为区间左端点变了,当然就增加了
    这样,我们从小到大枚举完 k k k 之后,再从大到小枚举 k k k
    (我自己的理解) 从小到大的理由是,我们必须要满足 i − l < l o w b i t ( x ) i-l<lowbit(x) il<lowbit(x) ,这样每次能填 2 k 2^k 2k 个数字;
    从大到小的理解就是我们必须填满 [ L , R ] [L,R] [L,R]
  • 然后考虑标记 m a r k [ i ] [ j ] mark[i][j] mark[i][j] 怎么转移
    容易想到,能转移成: m a r k [ i ] [ j − 1 ] , m a r k [ i + 2 j − 1 ] [ j − 1 ] mark[i][j-1],mark[i+2^{j-1}][j-1] mark[i][j1],mark[i+2j1][j1]
    然后再令 [ i + 2 k − 1 , i + 2 k − 1 ] ⊕ 2 k − 1 [i+2^{k-1},i+2^k-1]\oplus 2^{k-1} [i+2k1,i+2k1]2k1
    也就是我们需要正序枚举 i i i ,倒序枚举 j j j
    请添加图片描述

(或多或少或的或打错了,但是我发现图画错了,所以我修改了…)

代码

  • 时间复杂度: O ( n + q log ⁡ n ) O(n+q\log n) O(n+qlogn)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 6e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

int aa[MAX];
int bb[MAX];
bool mark[MAX][25];
int p2[25];
int main()
{
    p2[0] = 1;
    for(int i = 1;i <= 20;++i)p2[i] = p2[i-1] << 1;
    int n,Q;
    n = read();Q = read();
    for(int i = 1;i <= n;++i)aa[i] = read();
    while(Q--){
        int op,ta,tb,tc;
        op = read();ta = read();tb = read();tc = read();
        if(op == 0){
            bb[ta] ^= tc;
            bb[tb+1] ^= tc;
        }else{
            for(int i = 0;i <= 19;++i){
                if((tc & p2[i]) && ta + p2[i] - 1 <= tb){		// 注意左区间不要超过右区间了
                    mark[ta][i] ^= 1;
                    bb[ta] ^= tc;
                    bb[ta+p2[i]] ^= tc;
                    ta += p2[i];
                    tc += p2[i];
                }
            }
            for(int i = 19;i >= 0;--i){
                if(ta + p2[i] - 1 <= tb){
                    mark[ta][i] ^= 1;
                    bb[ta] ^= tc;
                    bb[ta+p2[i]] ^= tc;
                    ta += p2[i];
                    tc += p2[i];
                }
            }
        }
    }
    for(int i = 1;i <= n;++i){
        for(int j = 19;j >= 1;--j){
            if(mark[i][j]){			// 标记下推
                mark[i][j-1] ^= 1;
                if(i+p2[j-1] > n)continue;	// 注意不要越界
                mark[i+p2[j-1]][j-1] ^= 1;
                bb[i+p2[j-1]] ^= p2[j-1];
                if(i+p2[j] <= n)
                    bb[i+p2[j]] ^= p2[j-1];
            }
        }
    }
    for(int i = 1;i <= n;++i){
        bb[i] ^= bb[i-1];
        Print(aa[i] ^ bb[i],' ');
    }
    Putchar('\n');
    Write();
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值