【BZOJ2656】 [Zjoi2012]数列(sequence)
2656: [Zjoi2012]数列(sequence)
Time Limit: 2 Sec Memory Limit: 128 MBSubmit: 1485 Solved: 779
[Submit][Status][Discuss]
Description
小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式:
小白作为一个数学爱好者,很快就计算出了这个数列的通项公式。于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来。于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了一个绝妙的方法:即让小蓝说一个正整数N,小白则说出 的值,如果当N很大时小白仍能很快的说出正确答案,这就说明小白的确得到了公式。但这个方法有一个很大的漏洞:小蓝自己不会做,没法验证小白的答案是否正确。作为小蓝的好友,你能帮帮小蓝吗?
Input
输入文件第一行有且只有一个正整数T,表示测试数据的组数。
第2~T+1行,每行一个非负整数N。
Output
输出文件共包含T行。
第i行应包含一个不含多余前缀0的数,它的值应等于An(n为输入数据中第i+1行被读入的整数)
【样例输入】
Sample Input
3
1
3
10
1
3
10
Sample Output
1
2
3
2
3
HINT
T<=20,N<=10^100
Solution
Python裸题…。然而作为c++选手,也不想用记忆化搜索,那么要怎么弄呢。
主要是dyc大爷讲题时说了他不用记忆化的方法虽然我没有听懂 ,于是也自己YY了一下。
假设我们要算n=47的答案,那么我们需要知道n=23和n=24的答案,接下来我们就要知道n=11和n=12的答案。
也就是递归每一层都只需要知道两个数的答案就可以了,并且这两个数一定是连续的。
那么暴力弄出每一层需要知道的数然后自底向上算不就好了。
当n为奇数时,把上一层两个数Ai 和Ai+1 加起来就行了。
n为偶数就记一下每层两个数,i+1是奇数,还是i是奇数。然后我们就知道A2i对应哪个数了。
总而言之就是乱搞………………。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <cstdlib> using namespace std ; struct Bigdata{ int len , v[1100] ; Bigdata() { memset( v , 0 , sizeof(v)) ; len = 0 ; } }; Bigdata operator + ( Bigdata a , Bigdata b ) { Bigdata c ; c.len = max ( a.len , b.len ) ; for ( int i = 1 ; i <= c.len ; i ++ ) { c.v[i] += a.v[i] + b.v[i] ; if ( c.v[i] >= 10000 ) c.v[i] -= 10000 , c.v[i + 1] ++ ; } if ( c.v[c.len + 1] ) c.len ++ ; return c ; } Bigdata operator / ( Bigdata a , int b ) { Bigdata c ; c.len = a.len ; int x = 0 ; for ( int i = c.len ; i >= 1 ; -- i ) { c.v[i] = ( a.v[i] + x ) / b ; x = ( a.v[i] + x ) % b ; x *= 10000 ; } while ( !c.v[c.len] && c.len > 0 ) c.len -- ; return c ; } Bigdata p[2][1010] , ans[2][1010] , o; int q[1010] ; char s[110] ; void get_num() { scanf("%s",s) ; p[0][0].len = 0 ; int n = strlen(s) ; for ( int i = n - 1 ; i >= 0 ; i -= 4 ) { int res = 0 ; for ( int j = max ( i - 3 , 0 ) ; j <= i ; ++ j ) res = res * 10 + s[j] - '0' ; p[0][0].v[++p[0][0].len] = res ; } } void output ( Bigdata a ) { printf ( "%d" , a.v[a.len] ) ; for ( int i = a.len - 1 ; i >= 1 ; -- i ) if ( a.v[i] >= 1000 ) printf( "%d" , a.v[i] ) ; else if ( a.v[i] >= 100 ) printf( "0%d" , a.v[i] ) ; else if ( a.v[i] >= 10 ) printf( "00%d" , a.v[i] ) ; else printf( "000%d" , a.v[i] ) ; printf ( "\n" ) ; } void slove( ) { int l = 0 , x = 0 , y = 0 ; if ( p[0][0].v[1] & 1 ) y = 1 , p[1][0] = p[0][0] ; else y = 0 , p[1][0] = p[0][0] + o ; q[0] = 1 ; x = 1 ; for ( ; ; ) { if ( p[0][l].len <=1 && p[0][l].v[1] <= 1 && p[1][l].len <= 1 && p[1][l].v[1] <= 1 ) { ans[0][l] = p[0][l] , ans[1][l] = p[1][l] ; break ; } l ++ ; p[0][l] = p[x][l - 1] / 2 ; p[1][l] = p[0][l] + o ; if ( p[0][l].v[1] & 1 ) x = 0 ; else x = 1 ; q[l] = x ; } while ( l > 0 ) { l -- ; ans[0][l] = ans[q[l] ^ q[l + 1] ][l + 1] ; ans[1][l] = ans[0][l + 1] + ans[1][ l + 1 ] ; } x = y ; output ( ans[x][0] ) ; } int main() { freopen ( "1.in" , "r" , stdin ) ; freopen ( "1.out" , "w" , stdout ) ; int T ; scanf( "%d" , &T) ; o.v[1] = 1 , o.len = 1 ; while( T -- ) { get_num() ; slove() ; } return 0 ; }