2021牛客暑期多校训练营3 Kuriyama Mirai and Exclusive Or
题意:
长度为n的序列,支持两个操作,第一个为区间异或常数,第二个为区间异或公差为1的等差数列
最后询问一次序列
第一个异或常数是简单的,正常的数据结构都支持,而注意到本题是可以离线的,且只最后询问一次,可以使用差分的方法
第二个异或等差数列比较困难,可以拆分考虑
我们首先维护一个标记tg,tg[x][i]表示将x往后\(2^i\)个元素依次异或0,1,2,...,\(2^i-1\)
注意到按位考虑时,第i位是连续的\(2^{i-1}\)个0,\(2^{i-1}\)个1
因而这个标记在下放时,分裂为前后两段开头位置的新标记tg[x][i-1]和tg[x+(1<<(i-1))][i-1]
同时,要给后一段异或上\(2^i\),这个化为操作一,即差分维护
对于一般的区间如何做呢?分两部分来考虑,设x的lowbit为v,则[x,x+v)这部分数的加法可以等同于异或(因为没有进位)
从而,我们要做的操作是区间异或常数再异或等差数列,常数采用差分维护,而区间长度正好是2的幂,用tg维护
做完[x,x+v)这一段以后,我们要做的是x+v开头的一段,实际上也就是再异或一个常数v,然后重复进行上述操作即可
最后做完,下放标记,对差分数组做前缀和
#include <bits/stdc++.h>
using namespace std;
int rd() {
int ret = 0, f = 1;
char c;
while (c = getchar(), !isdigit(c))
f = c == '-' ? -1 : 1;
while (isdigit(c))
ret = ret * 10 + c - '0', c = getchar();
return ret * f;
}
const int MAXN = 600005;
int n, m;
int a[MAXN];
int d[MAXN];
bool tg[MAXN][32];
int main() {
n = rd();
m = rd();
for (int i = 1; i <= n; i++)
a[i] = rd();
int type, x, y, w;
for (int i = 1; i <= m; i++) {
type = rd();
x = rd();
y = rd();
w = rd();
if (type == 0) {
d[x] ^= w;
d[y + 1] ^= w;
} else {
for (int i = 0; i <= 30; i++) {
if (!((1 << i)&w))
continue;
if (x + (1 << i) - 1 > y)
continue;
tg[x][i] ^= 1;
d[x] ^= (w >> i) << i;
d[x + (1 << i)] ^= (w >> i) << i;
x += (1 << i);
w += (1 << i);
}
for (int i = 30; i >= 0; i--) {
if ((x + (1 << i) - 1) > y)
continue;
tg[x][i] ^= 1;
d[x] ^= (w >> i) << i;
d[x + (1 << i)] ^= (w >> i) << i;
x += (1 << i);
w += (1 << i);
}
}
}
for (int i = 30; i >= 1; i--) {
for (int j = 1; j <= n; j++) {
if (!tg[j][i])
continue;
tg[j][i - 1] ^= 1;
if (j + (1 << (i - 1)) <= n) {
tg[j + (1 << (i - 1))][i - 1] ^= 1;
d[j + (1 << (i - 1))] ^= (1 << (i - 1));
if (j + (1 << i) <= n) {
d[j + (1 << i)] ^= (1 << (i - 1));
}
}
}
}
for (int i = 1; i <= n; i++)
d[i] ^= d[i - 1];
for (int i = 1; i <= n; i++)
__builtin_printf("%d ", a[i]^d[i]);
return 0;
}
本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/15081211.html