【TJOI2017】異或和


題目描述

在加里敦中學的小明最近愛上了數學競賽,很多數學競賽的題目都是與序列的連續和相關的。所以對於一個序列,求出它們所有的連續和來說,小明覺得十分的簡單。但今天小明遇到了一個序列和的難題,這個題目不僅要求你快速的求出所有的連續和,還要快速的求出這些連續和的異或值。小明很快的就求出了所有的連續和,但是小明想考考你,在不告訴連續和的情況下,讓你快速的求出序列所有的連續和的異或值。

輸入格式

第一行輸入一個\(n\),表示這序列的數字個數。

第二行輸入\(n\)個數字\(a_1,a_2,a_3\)...\(a_n\),代表這個序列。

\(0≤a_1,a_2,\)...\(,a_n,0≤a_1+a_2+\)...\(+a_n≤10^6\)

輸出格式,

輸出這個序列所有的連續和的異或值。

數據范圍

對於\(20\%\)的數據,\(1≤n≤1000\)
對於\(100\%\)的數據,\(1≤n≤10^5\)


直接暴力\(O(n^2)\),可以獲得\(20\)

考慮按位枚舉,題目保證了\(0≤a_1+a_2+\)...\(+a_n≤10^6\)

那么顯然,我們最多只需要枚舉\(20\)

對於枚舉的每一位,我們希望快速地算出有多少和在這一位上有貢獻\(1\)

顯然,一個和我們可以通過前綴和的預處理,\(\sum_{i=l}^r=sum[r]-sum[l-1]\)

當我們知道了當前\(x\)\(sum[x]\)的第\(i\)位為\(1\)的時候,我們要知道以這一位為結束區間有多少貢獻為\(1\)

顯然只有兩種情況,當\(sum[y](y<x)\)的第\(i\)位也是\(1\)的時候,\(y\)的后面幾位必須比\(x\)大才可以使得\(x\)的前面退位,使得區間\(\sum_{i=y-1}^x\)產生貢獻

\(sum[y](y<x)\)的第\(i\)位也是\(0\)的時候,\(y\)的后面幾位必須比\(x\)小才可以保住\(x\)的貢獻

而滿足這樣的兩個條件,我們可以用樹狀數組來簡單地維護一下

\(x\)的第\(i\)位為\(0\)的時候,一樣的分析一下就好了

還有一個細節就是,計算貢獻的時候,可能答案會超過int,那就讓他自然溢出好了,就是最后計算的時候注意取模的正負即可


#include<cstdio>  
#include<iostream>  
#include<algorithm>  
#include<cstdlib>  
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s)
{
	char c=nc();int len=0;
	for(;!((c>='A' && c<='Z')||(c>='a' && c<='z'));c=nc()) if (c==EOF) return 0;
	for(;((c>='A' && c<='Z')||(c>='a' && c<='z'));s[len++]=c,c=nc());
	s[len++]='\0';
	return len;
}

inline void read(char &x){
  for (x=nc();!(x=='?' || x=='+' || x=='-');x=nc());
}

int wt,ss[19];
inline void print(int x){
	if (x<0) x=-x,putchar('-'); 
	if (!x) putchar(48); else {
	for (wt=0;x;ss[++wt]=x%10,x/=10);
	for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
	if (x<0) x=-x,putchar('-');
	if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,a[100010],p2[100],c0[2000010],c1[2000010],ans[100];
const int M=20;

void change0(int x,int y)
{
	x++;
	while (x<=p2[M]) c0[x]+=y,x+=x&(-x);
}
void change1(int x,int y)
{
	x++;
	while (x<=p2[M]) c1[x]+=y,x+=x&(-x);
}
int query0(int x)
{
	x++;
	int res=0;
	while (x>0) res+=c0[x],x-=x&(-x);
	return res;
}
int query1(int x)
{
	x++;
	int res=0;
	while (x>0) res+=c1[x],x-=x&(-x);
	return res;
}

int main()
{
	read(n);
	for (int i=1;i<=n;i++)
		read(a[i]);
	p2[0]=1;
	for (int i=1;i<=M;i++) p2[i]=2*p2[i-1];
	memset(ans,0,sizeof(ans));
	for (int i=1;i<=M;i++)
	{
		int s=0;
		for (int j=1;j<=n;j++)
		{
			s+=a[j];
			if ((s&p2[i-1])!=0) ans[i]++;
			if ((s&p2[i-1])==0)
			{
				ans[i]+=query0(p2[i-1]-1)-query0(s%p2[i-1]);
				ans[i]+=query1(s%p2[i-1]);
			}
			else
			{
				ans[i]+=query0(s%p2[i-1]);
				ans[i]+=query1(p2[i-1]-1)-query1(s%p2[i-1]);
			}
			if ((s&p2[i-1])==0) change0(s%p2[i-1],1);else change1(s%p2[i-1],1);
		}
		s=0;
		for (int j=1;j<=n;j++)
		{
			s+=a[j];
			if ((s&p2[i-1])==0) change0(s%p2[i-1],-1);else change1(s%p2[i-1],-1);
		}
	}
	int res=0,s=1;
	for (int i=1;i<=M;i++)
		res+=s*abs(ans[i]%2),s*=2;
	print(res),puts("");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM