I-Kuriyama Mirai and Exclusive Or
不过diabolusexnihil大佬的题解有一部分写错了应该是:每次分裂标记
b
l
,
i
b_{l,i}
bl,i需要给数组
[
l
+
2
i
−
1
,
l
+
2
i
)
⊕
2
i
−
1
[l+2^{i-1},l+2^i)\oplus2^{i-1}
[l+2i−1,l+2i)⊕2i−1然后标记分裂成
b
l
,
i
−
1
,
b
l
+
2
i
−
1
,
i
−
1
b_{l,i-1},b_{l+2^{i-1},i-1}
bl,i−1,bl+2i−1,i−1
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
T res=0;T fg=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res*fg;
}
int n,q;
const int N=600010;
int a[N],tag[N];
bool b[N][22];
int main()
{
n=rd(),q=rd();
for(int i=1;i<=n;i++) a[i]=rd();
while(q--)
{
int op=rd(),l=rd(),r=rd(),x=rd();
if(!op) // 操作一直接差分打标记
{
tag[l]^=x,tag[r+1]^=x;
continue;
}
// 考虑区间[l,l+2^i) 每次考虑lowbit(x) ^(x+i) -> ^x^i
for(int i=0;i<=19;i++)
if((x>>i&1)&&(l+(1<<i)-1)<=r)// 注意右端点
{
b[l][i]^=1;
tag[l]^=(x>>i)<<i;
tag[l+(1<<i)]^=(x>>i)<<i;//差分打标记
l+=(1<<i);
x+=(1<<i);
}
// 最后一段区间[l,r]
// 此时如果l+2^i<r 那么 x>>i&1一定是0 同样 ^(x+i) -> ^x^i效仿上面做法即可
for(int i=19;i>=0;i--)
if((l+(1<<i)-1)<=r)
{
b[l][i]^=1;
tag[l]^=(x>>i)<<i;
tag[l+(1<<i)]^=(x>>i)<<i;
l+=(1<<i);
x+=(1<<i);
}
}
// 标记分裂
for(int i=19;i>=1;i--)
for(int j=1;j<=n;j++)
{
if(!b[j][i]) continue;
b[j][i-1]^=1;// 分裂标记1
if(j+(1<<(i-1))<=n)
{
b[j+(1<<(i-1))][i-1]^=1;// 分裂标记2
// [l+2^{i-1},l+2^i) 需要打上抑或 2^{i-1}的标记
tag[j+(1<<(i-1))]^=(1<<(i-1));
if(j+(1<<i)<=n) tag[j+(1<<i)]^=(1<<(i-1));
}
}
for(int i=1;i<=n;i++)
{
tag[i]^=tag[i-1];
printf("%d%c",a[i]^tag[i]," \n"[i==n]);
}
}
一辈子学不会的做法www