题意
- 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+i−L)
问你 q q q 次操作完之后,每个位置的值 -
1
≤
n
≤
6
⋅
1
0
5
1\le n\le 6\cdot 10^5
1≤n≤6⋅105
1 ≤ q ≤ 4 ⋅ 1 0 5 1\le q\le 4\cdot 10^5 1≤q≤4⋅105
0 ≤ A i < 2 30 0\le A_i<2^{30} 0≤Ai<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 i−l<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+i−L)=a⊕(x∣(i−L))=a⊕x⊕(i−L)
对于左边的 ⊕ 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\\ ai⊕0ai+1⊕1ai+2⊕2⋮ai+2k−1⊕2k−1
这样操作完之后,相当于我们处理完了区间 [ L , L + 2 k − 1 ] [L,L+2^k-1] [L,L+2k−1]
我们让 L = L + 2 k , x = x + 2 k L=L+2^k,x=x+2^k L=L+2k,x=x+2k ,然后继续处理
就是区间左端点下标增加; x x x 增加了是因为区间左端点变了,当然就增加了
这样,我们从小到大枚举完 k k k 之后,再从大到小枚举 k k k
(我自己的理解) 从小到大的理由是,我们必须要满足 i − l < l o w b i t ( x ) i-l<lowbit(x) i−l<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][j−1],mark[i+2j−1][j−1]
然后再令 [ i + 2 k − 1 , i + 2 k − 1 ] ⊕ 2 k − 1 [i+2^{k-1},i+2^k-1]\oplus 2^{k-1} [i+2k−1,i+2k−1]⊕2k−1
也就是我们需要正序枚举 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;
}