DTOJ 1184:数列(sequence)
【题目描述】
小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式:
小白作为一个数学爱好者,很快就计算出了这个数列的通项公式。于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来。于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了一个绝妙的方法:即让小蓝说一个正整数N,小白则说出AN的值,如果当N很大时小白仍能很快的说出正确答案,这就说明小白的确得到了公式。但这个方法有一个很大的漏洞:小蓝自己不会做,没法验证小白的答案是否正确。作为小蓝的好友,你能帮帮小蓝吗?
【输入】
第一行有且只有一个正整数T,表示测试数据的组数。
第2~T+1行,每行一个非负整数N。
【输出】
包含T行。
第i行应包含一个不含多余前缀0的数,它的值应等于 (n为输入数据中第i+1行被读入的整数)
【样例输入】
3
1
3
10
【样例输出】
1
2
3
【分析】
对于每个数,需要考虑它是奇数还是偶数。若是偶数,也可以化为奇数。而最终所有的数都会化到0和1。不妨设原数为K1*A[1]+k2*A[0],则它必定是由某一个数转化而来。不难发现,k1*A[2i]+k2*A[2i+1]=(k1+k2)*A[i]+k2*A[i+1],另一种情况也类似。所以按照这个式子不断化简即可。
【代码】
#include<bits/stdc++.h>
using namespace std;
class INT
{
public:
int l,nm[120];
inline void read ( void )
{
char ch[120];scanf(" %s",ch+1);
l=strlen(ch+1);
for ( int i=1;i<=l;i++ ) nm[i]=ch[l-i+1]-48;
}
inline void write ( void )
{
for ( int i=l;i;i-- ) putchar(nm[i]+48);
putchar('\n');
}
inline bool is_one ( void ) { return (l==1)&&(nm[1]==1); }
inline bool is_odd ( void ) { return nm[1]&1; }
INT operator + ( const INT &x ) const
{
INT ret;ret.nm[1]=0;ret.l=max(l,x.l);
for ( int i=1;i<=ret.l;i++ )
{
ret.nm[i]+=(i<=l?nm[i]:0)+(i<=x.l?x.nm[i]:0);
ret.nm[i+1]=ret.nm[i]/10;ret.nm[i]%=10;
}
if ( ret.nm[ret.l+1] ) ret.l++;
return ret;
}
INT operator / ( const int &x ) const
{
INT ret;ret.l=l;ret.nm[l]=0;
for ( int i=l;i;i-- )
{
ret.nm[i]+=nm[i];
if ( i>1 ) ret.nm[i-1]=ret.nm[i]%x*10;
ret.nm[i]/=x;
}
while ( !ret.nm[ret.l] ) ret.l--;
return ret;
}
}x,y,a,b;
inline void sequence_main ( void )
{
x.read();a.l=b.l=a.nm[1]=b.nm[1]=1;
while ( !x.is_odd() ) x=x/2;
if ( x.is_one() ) { a.write();return; }
y=x/2;
while ( !y.is_one() )
if ( y.is_odd() ) y=y/2,b=a+b;
else y=y/2,a=a+b;
x=a+b;x.write();
}
int main(){int T;scanf("%d",&T);while(T--)sequence_main();return 0;}