BZOJ2656: [Zjoi2012]数列(sequence)
Description
小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式:
A(0)=0
A(1)=1
A(2i)=A(i) (对于任意 i>0)
A(2i+1)=A(i)+A(i+1) (对于任意 i>0)
小白作为一个数学爱好者,很快就计算出了这个数列的通项公式。
于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来。
于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了一个绝妙的方法:
即让小蓝说一个正整数N,小白则说出 的值,如果当N很大时小白仍能很快的说出正确答案,这就说明小白的确得到了公式。
但这个方法有一个很大的漏洞:小蓝自己不会做,没法验证小白的答案是否正确。
作为小蓝的好友,你能帮帮小蓝吗?
Input
输入文件第一行有且只有一个正整数T,表示测试数据的组数。
第2~T+1行,每行一个非负整数N。
Output
输出文件共包含T行。
第i行应包含一个不含多余前缀0的数,它的值应等于An(n为输入数据中第i+1行被读入的整数)
【样例输入】
Sample Input
1
3
10
Sample Output
2
3
HINT
T<=20,N<=10^100
题解Here!
先不管那个$n<=10^{100}$。。。
首先考虑一个问题:设$u imes A(i)+v imes A(i+1)=x imes A(frac{i}{2})+y imes A(frac{i}{2}+1)$,$i,u,v$已知,$x,y$未知。
求在$i$分别为奇数和偶数时$x,y$的一组正整数解。
我们可以想到:
1. $i$为偶数时,$i+1$为奇数。
此时有:$$u imes A(i)+v imes A(i+1)=u imes A(frac{i}{2})+v imes (A(frac{i}{2})+A(frac{i}{2}+1))=(u+v) imes A(frac{i}{2})+v imes A(frac{i}{2}+1)$$
2. $i$为奇数时,$i+1$为偶数。
此时有:$$u imes A(i)+v imes A(i+1)=u imes (A(frac{i}{2})+A(frac{i}{2}+1))+v imes A(frac{i}{2}+1)=u imes A(frac{i}{2})+(u+v) imes A(frac{i}{2}+1)$$
所以得出:
1. $i$为偶数时,$x=u+v,y=v$
2. $i$为奇数时,$x=u,y=u+v$
再回到原来的问题。
由于$10^{100}$,暴力递推肯定不行。
我们就考虑像倍增一样一倍一倍地递推。
首先不断地把$n$除以$2$,直到$n$不是$2$的倍数为止。
然后可以看出:$$Ans=A(frac{n}{2})+A(frac{n}{2}+1)$$。
此时,根据前面得出的结论,不断地把$n$缩小一倍,最后可以得到这样的式子:$$u imes A(0)+v imes A(1)$$。
而由于$A(0)=0,A(1)=1$,于是结果就是$v$。
高精实现即可。
但是这个高精好难写啊。。。
我表示再也不想写高精了。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 110 using namespace std; struct Bignum{ int len,val[MAXN]; void clean(){ len=0; memset(val,0,sizeof(val)); } void read(){ char c=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9'){val[++len]=c-'0';c=getchar();} for(int i=1;i<=len/2;i++)swap(val[i],val[len-i+1]); } void write(){ for(int i=len;i>=1;i--)printf("%d",val[i]); printf(" "); } friend Bignum operator +(Bignum x,int k){ int c=0; for(int i=1;i<=x.len&&k;i++){ x.val[i]+=k%10+c; k/=10; c=x.val[i]/10; x.val[i]%=10; } if(c)x.val[++x.len]+=c; while(k){ x.val[++x.len]=k%10; k/=10; } return x; } friend Bignum operator +(Bignum x,Bignum y){ int c=0,l=max(x.len,y.len); for(int i=1;i<=l;i++){ x.val[i]+=y.val[i]+c; c=x.val[i]/10; x.val[i]%=10; } x.len=l; if(c)x.val[++x.len]+=c; return x; } friend Bignum operator >>(Bignum x,const int k){ for(int i=x.len;i>=1;i--){ if(x.val[i]&1)x.val[i-1]+=10; x.val[i]>>=1; } while(x.len>1&&!x.val[x.len])x.len--; return x; } }n; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } Bignum solve(Bignum num){ if(num.len==1&&(num.val[1]==0||num.val[1]==1))return num; while((num.val[1]&1)==0)num=num>>1; Bignum l=num>>1,r=l+1,u,v; u.clean();v.clean();u.len=v.len=u.val[1]=v.val[1]=1; while(l.len>1||l.val[1]){ if(l.val[1]&1)v=u+v; else u=u+v; l=l>>1;r=l+1; } return v; } int main(){ int t=read(); while(t--){ n.clean();n.read(); n=solve(n); n.write(); } return 0; }