[BZOJ2656][ZJOI2012]数列(递推+高精)

首先考虑一个问题,设 uA(i)+vA(i+1)=xA(i/2)+yA(i/2+1) i,u,v 为已知量, x,y 为未知量,求在 i 分别为奇数和偶数时x,y的一组正整数解。可以想到:
i 为偶数时,i+1为奇数。此时 uA(i)+vA(i+1)=uA(i/2)+v(A(i/2)+A(i/2+1))=(u+v)A(i/2)+vA(i/2+1)
i 为奇数时,i+1为偶数。此时 uA(i)+vA(i+1)=u(A(i/2)+A(i/2+1))+vA(i/2+1)=uA(i/2)+(u+v)A(i/2+1)
所以得出: i 为偶数时,x=u+v,y=v,否则 i 为奇数时,x=u,y=u+v
回到原问题。由于 N<=10100 的数据范围,一步一步地递推肯定是行不通的。我们就考虑一倍一倍地递推。
首先不断地把 N 除以2直到 N 不是2的倍数为止。
然后可以看出,结果 =1A(N/2)+1A(N/2+1) 。此时,根据前面得出的结论,不断地把 N 缩小一倍,最后可以得到uA(0)+vA(1)的式子。到了这一步,就可以得出结果为 v <script type="math/tex" id="MathJax-Element-781">v</script>。高精实现即可。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 105;
struct cyx {
    int n, a[N];
    cyx() {}
    cyx(int _n) :
        n(_n) {memset(a, 0, sizeof(a));}
};
cyx read() {
    int i = 1, j; cyx res = cyx(0); char c; bool flag = 0;
    while ((c = getchar()) < '0' || c > '9');
    if (c - 48) res.a[res.n = 1] = c - 48, flag = 1;
    while ((c = getchar()) >= '0' && c <= '9') {
        if (c - 48) flag = 1;
        if (flag) res.a[++res.n] = c - 48;
    }
    if (!res.n) res.a[res.n = 1] = 0;
    for (j = res.n; i < j; i++, j--) swap(res.a[i], res.a[j]);
    return res;
}
void write(cyx num) {
    int i; for (i = num.n; i; i--)
        printf("%d", num.a[i]);
    printf("\n");
}
cyx div2(cyx num) {
    int i; for (i = num.n; i; i--) {
        if (num.a[i] & 1) num.a[i - 1] += 10;
        num.a[i] >>= 1;
    }
    while (num.n > 1 && !num.a[num.n]) num.n--;
    return num;
}
cyx plus1(cyx num) {
    int i = 1; while (num.a[i] == 9) num.a[i] = 0, i++;
    num.a[i]++; if (num.a[num.n + 1]) num.n++; return num;
}
cyx add(cyx a, cyx b) {
    cyx res = cyx(max(a.n, b.n)); int i;
    for (i = 1; i <= res.n; i++) {
        res.a[i] += a.a[i] + b.a[i];
        if (res.a[i] > 9) res.a[i] -= 10, res.a[i + 1]++;
    }
    if (res.a[res.n + 1]) res.n++; return res;
}
cyx solve(cyx num) {
    if (num.n == 1 && (num.a[1] == 0 || num.a[1] == 1)) return num;
    while (!(num.a[1] & 1)) num = div2(num);
    cyx l = div2(num), r = plus1(l), u = cyx(1), v = cyx(1);
    u.a[1] = v.a[1] = 1; while (l.n > 1 || l.a[1]) {
        if (l.a[1] & 1) v = add(u, v); else u = add(u, v);
        l = div2(l); r = plus1(l);
    }
    return v;
}
int main() {
    int T; scanf("%d", &T);
    while (T--) write(solve(read()));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值