BZOJ2656 [Zjoi2012]数列

Description

小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式:

$$\begin{aligned}
A_0 &= 0\\
A_1 &= 1\\
A_{2i} &= A_i\\
A_{2i+1} &= A_i + A_{i+1}
\end{aligned}$$

小白作为一个数学爱好者,很快就计算出了这个数列的通项公式。于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来。于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了一个绝妙的方法:即让小蓝说一个正整数N,小白则说出 的值,如果当N很大时小白仍能很快的说出正确答案,这就说明小白的确得到了公式。但这个方法有一个很大的漏洞:小蓝自己不会做,没法验证小白的答案是否正确。作为小蓝的好友,你能帮帮小蓝吗? 

Input

输入文件第一行有且只有一个正整数,表示测试数据的组数。

第2~T+1行,每行一个非负整数N

Output

输出文件共包含T行。

第i行应包含一个不含多余前缀0的数,它的值应等于An(n为输入数据中第i+1行被读入的整数)

Sample Input

3
1
3
10

Sample Output

1
2
3

题解

手算几组小数据可以发现(也可以证明)在展开k次之后一定会得到$$xA_{\lfloor n/2^k \rfloor}+yA_{\lfloor n/2^k \rfloor+1}$$的形式,并且若

$$A_n = uA_i + vA_{i+1}$$,那么:

若i是偶数,则

$$A_n = (u+v)A_{i/2} + vA_{i/2+1}$$

若i是奇数,则

$$A_n = uA_{i/2} + (u+v)A_{i/2+1}$$

高精搞一搞即可。

代码:

#include <algorithm>
#include <cstdio>
#include <cstring>
const int N = 105;
int A[N], B[N], C[N];
char s[105];
void div2() {
  for (int i = A[0]; i; --i) {
    if (i && (A[i] & 1)) A[i - 1] += 10;
    A[i] >>= 1;
  }
  while (A[0] && !A[A[0]]) --A[0];
}
void add(int *a, int *b) {
  int t = 0;
  for (int i = 1; i <= b[0] || t; ++i) {
    t = (a[i] += b[i] + t) / 10;
    a[i] %= 10;
  }
  a[0] = std::max(a[0], b[0]);
  while (a[a[0] + 1]) ++a[0];
}
int main() {
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%s", s);
    if (!strcmp(s, "0")) {
      puts("0");
      continue;
    }
    A[0] = strlen(s);
    for (int i = 0; i < A[0]; ++i)
      A[A[0] - i] = s[i] - '0';
    memset(B, 0, sizeof B);
    memset(C, 0, sizeof C);
    B[0] = B[1] = 1;
    while (A[0]) {
      if (A[1] & 1) add(C, B);
      else add(B, C);
      div2();
    }
    for (int i = C[0]; i; --i)
      putchar(C[i] + '0');
    putchar('\n');
  }
  return 0;
}

  

posted @ 2017-08-16 15:12  _rqy  阅读(463)  评论(0编辑  收藏  举报