CF242E XOR on Segment (线段树)
CF242E XOR on Segment
这道题还蛮简单的。其实很经典。
看到 xor
,第一下想到的是 \(01\) Trie,但是很显然不支持区间修改。于是我们可以自然地想到线段树。我们在每个节点上可以维护当前区间的和,但是不是用一个数来表示,而是用二进制表示的。我们记 \(num_{1,i}\) 为这个区间第 \(i\) 位二进制有多少个 \(1\)。\(num_{0,i}\) 同理。为了方便统计,就不用进位了。显然 pushup
操作很简单,这里不再赘述。现在如果要警醒修改,设修改的区间是 \([l,r]\),修改的数是 \(val\)。在修改时,只要当前 \(val\) 这一位上二进制是 \(1\),就把 \(num_{1,i},num_{0,i}\) 数值交换一下即可。最后统计答案时只需要把二进制加起来即可。
时间复杂度 \(O(\log \{\max a_i\}\cdot n\log n)\)。
读者可以思考一下把 xor
换成 and
和 or
怎么做。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=1e5+10;
int n,m;
int a[MAXN];
struct seg_tree {
int lazy,num[2][21];
}s[MAXN<<2];
void pushdown(int l,int r,int p) {
if(l==r) {
return ;
}
s[p<<1].lazy^=s[p].lazy;
s[p<<1|1].lazy^=s[p].lazy;
for(int i=0;i<=20;i++) {
if(s[p].lazy&(1<<i)) {
swap(s[p<<1].num[0][i],s[p<<1].num[1][i]);
swap(s[p<<1|1].num[0][i],s[p<<1|1].num[1][i]);
}
}
s[p].lazy=0;
}
void build(int l,int r,int p) {
if(l==r) {
for(int i=0;i<=20;i++) {
s[p].num[a[l]&(1<<i)? 1:0][i]++;
}
s[p].lazy=0;
return ;
}
int mid=(l+r)>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
for(int i=0;i<=20;i++) {
s[p].num[1][i]=s[p<<1].num[1][i]+s[p<<1|1].num[1][i];
s[p].num[0][i]=s[p<<1].num[0][i]+s[p<<1|1].num[0][i];
}
}
void modify(int l,int r,int p,int x,int y,int val) {
pushdown(l,r,p);
if(r<x||y<l) {
return ;
}
if(x<=l&&r<=y) {
for(int i=0;i<=20;i++) {
if(val&(1<<i)) {
swap(s[p].num[0][i],s[p].num[1][i]);
}
}
s[p].lazy=val;
return ;
}
int mid=(l+r)>>1;
modify(l,mid,p<<1,x,y,val);
modify(mid+1,r,p<<1|1,x,y,val);
for(int i=0;i<=20;i++) {
s[p].num[1][i]=s[p<<1].num[1][i]+s[p<<1|1].num[1][i];
s[p].num[0][i]=s[p<<1].num[0][i]+s[p<<1|1].num[0][i];
}
}
int query(int l,int r,int p,int x,int y) {
pushdown(l,r,p);
if(r<x||y<l) {
return 0;
}
if(x<=l&&r<=y) {
int ret=0;
for(int i=0;i<=20;i++) {
ret+=(1<<i)*s[p].num[1][i];
}
return ret;
}
int mid=(l+r)>>1;
return query(l,mid,p<<1,x,y)+query(mid+1,r,p<<1|1,x,y);
}
signed main() {
cin>>n;
for(int i=1;i<=n;i++) {
a[i]=read();
}
build(1,n,1);
cin>>m;
for(int i=1;i<=m;i++) {
int opt=read();
if(opt==2) {
int x=read(),y=read(),val=read();
modify(1,n,1,x,y,val);
}
else {
int x=read(),y=read();
printf("%lld\n",query(1,n,1,x,y));
}
}
return 0;
}