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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章