51nod-1310: Chandrima and XOR
【传送门:51nod-1310】
简要题意:
有一个数组S,保证里面的数是从小到大的,而且每一个数的二进制中都没有连续的1,如:1,2,4,5,8...
给出n,然后给出n个位置,求出S数组中n个位置的异或和
题解:
数位DP好题,卡了老久
设f[i]表示2i-1到2i-1中有多少个数是在数组S中的
转移很容易想到$f[i]=1+\sum_{j=1}^{i-2}f[j]$
而也可以得到$f[i-1]=1+\sum_{j=1}^{i-3}f[j]$
上下两式合并得到$f[i]=f[i-1]+f[i-2]$
就是斐波那契数列,f[1]=f[2]=1
设s[i]为1到2i-1中有多少个数是在数组S中的,显然$s[i]=\sum_{j=1}^{i}f[j]$
然后就可以做了,对于一个位置x,二分找到最大的小于x的s[m],将x-s[m]-1后再进行递归二分,复杂度为O((logn)2)
只要在途中维护异或就行了,设P[i]为答案二进制中第i位的数,对于找到的m,将P[m]^=1就好了
最后转换成二进制就行了,注意可能在算2的次方的时候会爆long long,所以要用快速幂
参考代码:
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; int P[310],tot; LL a[51000],Mod=1e9+7; LL f[310],s[310]; void pre() { tot=2; f[1]=1;f[2]=1; s[1]=1;s[2]=2; for(tot=3;;tot++) { f[tot]=f[tot-2]+f[tot-1]; s[tot]=f[tot]+s[tot-1]; if(s[tot]>1e18) break; } } void getp(LL x) { if(x==0) return ; int l=0,r=tot,m; while(l<=r) { int mid=(l+r)/2; if(x>s[mid]) m=mid,l=mid+1; else r=mid-1; } P[m]^=1; getp(x-s[m]-1); } LL p_mod(LL a,LL b) { LL ans=1; while(b!=0) { if(b%2==1) ans=ans*a%Mod; a=a*a%Mod;b/=2; } return ans; } int main() { pre(); int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); getp(a[i]); } LL ans=0; for(int i=0;i<=tot;i++) ans=(ans+LL(P[i])*p_mod(2,i))%Mod; printf("%lld\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚