CodeForces - 1323D Present(思维+数学)

题目链接:点击查看

题目大意:给出一个数列 a ,求出

 (a_1 + a_2) \oplus (a_1 + a_3) \oplus \ldots \oplus (a_1 + a_n) \\ \oplus (a_2 + a_3) \oplus \ldots \oplus (a_2 + a_n) \\ \ldots \\ \oplus (a_{n-1} + a_n) \\

题目分析:如果暴力的话显然时间复杂度是 n * n 的,我们应该想办法去优化,比赛的时候想用线段树,但是不会在维护异或的前提下区间加法,也想过用矩阵维护,但丝毫没什么用呀,队友想到了可以按位维护,也就是维护26个线段树,我觉得太麻烦了就放弃这个题了,补题的时候看了题解,感觉题解已经说的很明白了,在这里再记录一下吧,感觉还是自己太菜了,需要多做题长见识

题解的意思是,可以按位维护,因为异或等位运算,最大的特点就是,每一位都可以视为独立的个体,互不影响,互不干预,这样我们在单独处理第 k 位的时候,因为要计算 a[ i ] + a[ j ] 后再进行异或,所以显然某个数大于 2^{k+1} 的部分是没有贡献的,我们可以先让每个数对 2^{k+1} 取模,因为后面我们需要二分查找,所以取模后对于整个数组排一下序,到此为止,所有的 a 数组中的每个元素的取值范围都是 [ 0 , 2^{k+1}-1] ,显然 a[ i ] + a[ j ] 的取值范围是 [0,2^{k+2}-2] ,不难看出第 k 位为 1 时的 a[ i ] + a[ j ] 的取值范围为 [2^k,2^{k+1}-1]\cup [2^{k+1}+2^k,2^{k+2}-2] ,这样我们就可以固定下来 i ,从而二分查找符合条件的 j ,记录下来有多少对 ( i , j ) 可以对第 k 位做出贡献,因为 a[ i ] + a[ j ] 的范围上面已经确定下来了,那么当 a[ i ] 确定时,a[ j ]的范围也就非常容易确定了:[2^k-a[i],2^{k+1}-1-a[i]]\cup [2^{k+1}+2^k-a[i],2^{k+2}-2-a[i]] 。如果 k 是奇数的话,那么答案的第 k 位就是 1 ,否则答案的第 k 位就是 0

上面是思路,具体实现的话可以用upper_bound和lower_bound搭配使用,并且注意二分的范围,如果每次都从[1,n]的范围二分,会导致答案重复,所以我们可以将范围控制为[1,i]就好了,i 为枚举的位置

然后因为上面的取模操作,我懒得再开一个新数组储存答案了,于是就在原数组上修改,不过这样的话就需要从最高位开始计算答案了,时间复杂度为 n * logn * logC ,C为1e7,也就是 a[ i ] 的最大值

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
      
typedef long long LL;
     
typedef unsigned long long ull;
      
const int inf=0x3f3f3f3f;
 
const int N=4e5+100;

int a[N];

int main()
{
#ifndef ONLINE_JUDGE
//	freopen("input.txt","r",stdin);
//	freopen("output.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	int ans=0;
	for(int i=26;i>=0;i--)
	{
		for(int j=1;j<=n;j++)
			a[j]&=(1<<(i+1))-1;
		sort(a+1,a+1+n);
		for(int j=1;j<=n;j++)
		{
			if((upper_bound(a+1,a+j,(1<<(i+1))-1-a[j])-lower_bound(a+1,a+j,(1<<i)-a[j]))&1)
				ans^=1<<i;
			if((upper_bound(a+1,a+j,(1<<(i+2))-2-a[j])-lower_bound(a+1,a+j,(1<<(i+1))+(1<<i)-a[j]))&1)
				ans^=1<<i;
		}
	}
	printf("%d\n",ans);
	
	
	
	
	
	
	
	
	
	
	
	
	return 0;
}
发布了691 篇原创文章 · 获赞 27 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/104730247