题目链接
解题思路
ducati说挺一眼的?不过我太弱了,想不到拆位线段树。
不过也许确实评不到这么高,也确实挺一眼的,至少用分块是这样。
现在,我们的分块要求我们解决这几个问题:
- 单点修改,如何修改这个块的标记?
- 整块修改,该如何打标记?
- 单点贡献及整块贡献如何计算?
整块修改
整块异或,我们直接开一个变量
x
i
x_i
xi,代表第
i
i
i 个块在计算贡献时每个数要异或
x
i
x_i
xi。
每次修改将
x
i
x_i
xi 异或上
x
x
x 即可。
单点贡献与整块贡献
单点贡献直接加那一点的值异或上
x
i
x_i
xi。
整块贡献:显然当
x
i
x_i
xi 的第
j
j
j 位为
0
0
0 时,二进制的第
j
j
j 位为
1
1
1 这一位才会产生贡献,否则此位为
0
0
0 才会产生贡献。那么我们可以事先统计出二进制第
j
j
j 位为
1
1
1 的数的个数
y
i
,
j
y_{i,j}
yi,j,然后就可以直接计算这一块有多少个数的第
j
j
j 位会产生
2
j
2^j
2j 的贡献。对每一位都如此计算并加和即可。
单点修改
直接把这一点异或上
x
x
x。
但是,请注意,同时也要修改该块的
y
i
y_i
yi。
直接异或,那么以前异或过的怎么办?
没事,异或有交换律。
分块复杂度为 O ( n log a i + m n log a i ) \Omicron(n\log a_i+m\sqrt n\log a_i) O(nlogai+mnlogai),劣于拆位线段树的 O ( m log n log a i ) \Omicron(m\log n\log a_i) O(mlognlogai)。但是由于常数的原因,跑出来差不了多少。只不过加起来差了约 7 s 7s 7s。
代码实现
#include <iostream>
#include <cmath>
#include <array>
using namespace std;
array<int,100180> nums;
array<array<int,20>,320> blocks;
array<int,320> xor_block;
int main(int argc,char* argv[],char* envp[])
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int cnt,cntquery,block_size,block_cnt;
cin>>cnt,block_size=sqrt(cnt),block_cnt=(cnt-1)/block_size+1;
auto idx_acs=[block_size](int i,int j){return (i-1)*block_size+j;};
for(int i=1;i<=cnt;i++)
cin>>nums[i];
for(int i=1;i<=block_cnt;i++)
for(int j=1;j<=block_size;j++)
for(int k=1,l=0;k<=nums[idx_acs(i,j)];k<<=1,++l)
blocks[i][l]+=(bool)(nums[idx_acs(i,j)]&k);
cin>>cntquery;
while(cntquery--)
{
int type,range_l,range_r;
cin>>type>>range_l>>range_r;
if(type==1)
{
long long ans=0;
if((range_l-1)/block_size==(range_r-1)/block_size)
{
for(int i=range_l;i<=range_r;i++)
ans+=nums[i]^xor_block[(i-1)/block_size+1];
goto output;
}
for(int i=range_l;i%block_size!=1&&i<=range_r;i++)
ans+=nums[i]^xor_block[(i-1)/block_size+1];
for(int i=(range_l-1)/block_size+1+(range_l%block_size!=1);i*block_size<=range_r;i++)
for(int k=1,l=0;l<20;k<<=1,++l)
ans+=k*(xor_block[i]&k?block_size-blocks[i][l]:blocks[i][l]);
for(int i=range_r;i%block_size;i--)
ans+=nums[i]^xor_block[(i-1)/block_size+1];
output:cout<<ans<<'\n';
}
else
{
int tmp;
cin>>tmp;
if((range_l-1)/block_size==(range_r-1)/block_size)
{
for(int i=range_l;i<=range_r;i++)
{
for(int k=1,l=0;k<=nums[i];k<<=1,++l)
blocks[(i-1)/block_size+1][l]-=(bool)(nums[i]&k);
nums[i]^=tmp;
for(int k=1,l=0;k<=nums[i];k<<=1,++l)
blocks[(i-1)/block_size+1][l]+=(bool)(nums[i]&k);
}
goto ended;
}
for(int i=range_l;i%block_size!=1&&i<=range_r;i++)
{
for(int k=1,l=0;k<=nums[i];k<<=1,++l)
blocks[(i-1)/block_size+1][l]-=(bool)(nums[i]&k);
nums[i]^=tmp;
for(int k=1,l=0;k<=nums[i];k<<=1,++l)
blocks[(i-1)/block_size+1][l]+=(bool)(nums[i]&k);
}
for(int i=(range_l-1)/block_size+1+(range_l%block_size!=1);i*block_size<=range_r;i++)
xor_block[i]^=tmp;
for(int i=range_r;i%block_size;i--)
{
for(int k=1,l=0;k<=nums[i];k<<=1,++l)
blocks[(i-1)/block_size+1][l]-=(bool)(nums[i]&k);
nums[i]^=tmp;
for(int k=1,l=0;k<=nums[i];k<<=1,++l)
blocks[(i-1)/block_size+1][l]+=(bool)(nums[i]&k);
}
ended:;
}
}
return 0;
}