NOIP2023模拟13联测34 origen

文章讲述了如何解决一个关于整数序列中异或操作后平方和的问题,通过位运算和向量存储优化,降低了时间复杂度至O(nlog^2n)。

题目大意

给定nnn个整数a1,a2,…,ana_1,a_2,\dots,a_na1,a2,,an,求

∑i=1n∑j=in(⨁k=ijak)2\sum\limits_{i=1}^n\sum\limits_{j=i}^n(\bigoplus\limits _{k=i}^ja_k)^2i=1nj=in(k=ijak)2

输出答案模998244353998244353998244353后的值。

注: ⨁k=ijak=ai⊕ai+1⊕⋯⊕aj\bigoplus\limits _{k=i}^ja_k=a_i\oplus a_{i+1}\oplus \cdots \oplus a_jk=ijak=aiai+1aj

1≤n≤2×105,1≤ai≤2×1051\leq n\leq 2\times 10^5,1\leq a_i\leq 2\times 10^51n2×105,1ai2×105


题解

di=⨁j=1iajd_i=\bigoplus\limits _{j=1}^ia_jdi=j=1iaj,则原式变为

∑i=1n∑j=in(dj⊕di−1)2\sum\limits_{i=1}^n\sum\limits_{j=i}^n(d_j\oplus d_{i-1})^2i=1nj=in(djdi1)2

我们按位来考虑,枚举每一位kkk,那么原式变为

∑k=0mx2k∑(dj⊕di−1)&2k=2k(dj⊕di−1)\sum\limits_{k=0}^{mx}2^k\sum\limits_{(d_j\oplus d_{i-1})\& 2^k=2^k}(d_j\oplus d_{i-1})k=0mx2k(djdi1)&2k=2k(djdi1)

其中mxmxmx表示所有did_idi的二进制最高位的是从低到高的第几位。

我们可以用vectorvectorvector来存每一位为000或为111的数。也就是说,对于每个数did_idi,枚举它的每一位kkk,如果这一位为000,则将did_idi放在vk,0v_{k,0}vk,0中;如果这一位为111,则将did_idi放在vk,1v_{k,1}vk,1中。这里的vk,0/1v_{k,0/1}vk,0/1vectorvectorvector类型的。

然后,枚举每一个kkk,我们要求的就是∑(dj⊕di−1)&2k=2k(dj⊕di−1)\sum\limits_{(d_j\oplus d_{i-1})\& 2^k=2^k}(d_j\oplus d_{i-1})(djdi1)&2k=2k(djdi1)。既然要异或后的第kkk位为111,那么djd_jdjdi−1d_{i-1}di1的第kkk位肯定不同。那么,我们需要求vk,0v_{k,0}vk,0vk,1v_{k,1}vk,1中各取一个数来异或,然后将所有情况求和。

这怎么处理呢?我们还是按每一位来计算。枚举每一位ttt,如果异或和的第ttt位为111,则在vk,0v_{k,0}vk,0中选的数和vk,1v_{k,1}vk,1中选的数的第ttt位肯定不同。那么,我们记录vk,0v_{k,0}vk,0中的每一位ttt有多少个数为000,多少个数为111,记为hvt,0/1hv_{t,0/1}hvt,0/1,那么对于vk,1v_{k,1}vk,1中的每一位ttt,设这一位为vvvvvv的值为000111),其贡献为2t×hvt,1−v2^t\times hv_{t,1-v}2t×hvt,1v

注意d0d_0d0也要加入vectorvectorvector。因为这是vk,0v_{k,0}vk,0中的数异或vk,1v_{k,1}vk,1中的数,所以是有序的,不需要除以222

时间复杂度为O(nlog⁡2n)O(n\log^2 n)O(nlog2n)

code

#include<bits/stdc++.h>
using namespace std;
const int N=200000;
const long long mod=998244353;
int n,a[N+5],b[N+5],hv[25][2];
long long ans=0;
vector<int>v[25][2];
int main()
{
//	freopen("origen.in","r",stdin);
//	freopen("origen.out","w",stdout);
	scanf("%d",&n);
	for(int j=0;j<=19;j++){
		v[j][0].push_back(0);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=b[i-1]^a[i];
		for(int j=0;j<=19;j++){
			v[j][(b[i]>>j)&1].push_back(b[i]);
		}
	}
	for(int i=0;i<=19;i++){
		for(int j=0;j<=19;j++) hv[j][0]=hv[j][1]=0;
		for(int j=0;j<v[i][0].size();j++){
			int t=v[i][0][j];
			for(int k=0;k<=19;k++){
				++hv[k][(t>>k)&1];
			}
		}
		long long tmp=0;
		for(int j=0;j<v[i][1].size();j++){
			int t=v[i][1][j];
			for(int k=0;k<=19;k++){
				int vk=((t>>k)&1)^1;
				tmp=(tmp+1ll*hv[k][vk]*(1<<k))%mod;
			}
		}
		ans=(ans+tmp*(1<<i))%mod;
	}
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值