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

本文介绍了一种处理高精度数值计算的问题解决方法,通过不断将问题规模减半来求解特定形式的线性组合问题。适用于数据规模非常大的情况,如N可达10^100级别。文中提供了详细的算法思路及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先考虑一个问题,设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。高精实现即可。
代码:

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值