Solution:CF242E(XOR on Segment)

题目链接

Link:CF242E

解题思路

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+mn logai),劣于拆位线段树的 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值