[51nod1310]Chandrima and XOR

  有这样一个小到大排列的无穷序列S:1, 2, 4, 5, 8......,其中任何一个数转为2进制不包括2个连续的1。给出一个长度为N的正整数数组A,A1, A2......An记录的是下标(下标从1开始)。求S[A1] Xor S[A2] Xor S[A3] ..... Xor S[An]的结果(Xor 为异或运算),由于该数很大,输出Mod 1000000007的结果。

  例如:A = {1, 2, 3},对应S[1] = 1, S[2] = 2, S[3] = 4。1 Xor 2 Xor 4 = 7。
 Input
  第1行:1个数N,表示数组A的长度(1 <= N <= 50000)。
  第2 - N + 1行:每行一个数,对应数组A的元素A[i](1 <= A[i] <= 10^18)。
 Output
  输出一个数,S[A1] Xor S[A2] Xor S[A3] ..... Xor S[An]的结果Mod 1000000007。

 

 

  完全不会搞只能膜题解系列。

实际上这道题可以通过O((logn)^2)的时间推出任意一项。

我们以每一个2的整数次方作为分割点,把这个数列分割成很多块。设F(n)为2^n到2^(n+1)之间的所有满足要求的数字(不包括2^(n+1))。因为二进制中不能有连续的0,所以F(n)=sigma{F(k)|0<=k<=n-2}+1,又知F(n-1)=sigma{F(k)|0<=k<=n-3}+1,可得F(n)=F(n-2)+F(n-1)-1+1=F(n-1)+F(n-2),因此结论:

有1个数在[1,2),1个数在[2,4),2个数在[4,8),3个数在[8,16)…………

而每次可以用二分查找来确定a的位置在2^m和2^(m+1)之间,a减去[1,2^m]之间所有数的个数,重新递归查找。。。(注意减去的是闭区间!)递归深度不会超过logn,因为它是按斐波那契数列的和递减的。每一次递归的m位置异或1,最后会得到一个01数组,按照进制转换然后求模就行了。。

一共有n个数,对于每一次查找的复杂度是O((logn)^2),总复杂度不会达到O(n(logn)^2)。

  讲道理那个查找是一个log的....

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cmath>
 7 #include<cstdlib>
 8 #include<bitset>
 9 //#include<ctime>
10 #define ll long long
11 #define ull unsigned long long
12 #define ui unsigned int
13 #define d double
14 //#define ld long double
15 using namespace std;
16 const int maxn=233,modd=1000000007;
17 ll pre[maxn],f[maxn],two[maxn];
18 int i,j,k,n,m,MX;
19 bool u[90];
20 
21 int ra,fh;char rx;
22 inline int read(){
23     rx=getchar(),ra=0,fh=1;
24     while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();
25     if(rx=='-')fh=-1,rx=getchar();
26     while(rx>='0'&&rx<='9')ra=ra*10+rx-48,rx=getchar();return ra*fh;
27 }
28 
29 inline int get(ll x){
30     int i;
31     for(i=MX;i>0&&x>1;i--)if(x<=pre[i]&&x>pre[i-1])x-=pre[i-1]+1,u[i]^=1,i--;//,printf("   %d\n",i);
32     u[0]^=x>0;
33 }
34 int main(){
35     f[0]=f[1]=1,two[0]=pre[0]=1,two[1]=pre[1]=2;
36     for(i=2;i<=233;i++){
37         f[i]=f[i-2]+f[i-1],two[i]=(two[i-1]<<1)%modd,
38         pre[i]=pre[i-1]+f[i];
39         if(pre[i]>1e18)break;
40     }MX=i;
41     
42     n=read();ll a;int ans=0;
43 //    for(i=1;i<=233;i++)printf("   %d\n",get(i,MX));
44     for(i=1;i<=n;i++)scanf("%lld",&a),get(a);
45     for(i=0;i<=MX;i++)if(u[i])(ans+=two[i])%=modd;
46     printf("%d\n",ans);
47 }
View Code

 

posted @ 2016-10-13 18:42  czllgzmzl  阅读(311)  评论(0编辑  收藏  举报