UVA 766 伯努利公式

UVA 766 伯努利公式

题意

S k ( n ) = ∑ i = 1 n i k S_k(n)=\sum_{i=1}^n i^k Sk(n)=i=1nik

找到最小的M,以及 a k , . . . a 1 a_k,...a_1 ak,...a1 使得上式变成下式
S k ( n ) = 1 M ( a k + 1 ∗ n k + 1 + a k n k + . . . + a 1 ∗ n + a 0 ) S_k(n)=\frac{1}{M}(a_{k+1}*n^{k+1} +a_kn^k+...+a_1*n+a_0) Sk(n)=M1(ak+1nk+1+aknk+...+a1n+a0)

题解

主要思路

该题就是套公式,公式理解都来自于这两个链接。

wiki上关于伯努利数的介绍

知乎上对伯努利数的推导

定义等幂和如下, 其中 m , n ≥ m,n \geq m,n 0:
S k ( n ) = ∑ i = 1 n i k = 1 k + 2 k + . . . n k S_k(n)=\sum_{i=1}^n i^k=1^k+2^k+...n^k Sk(n)=i=1nik=1k+2k+...nk
这数列和的公式必定是变量为n,次数为m+1次的多项式,称为伯努利多项式

伯努利多项式的系数伯努利数 B n B_n Bn 有密切关系如下:
S k ( n ) = 1 k + 1 ∑ i = 0 k C k + 1 i   B i   n k + 1 − i S_k(n)=\frac{1}{k+1} \sum_{i=0}^kC_{k+1}^i\,B_i\,n^{k+1-i} Sk(n)=k+11i=0kCk+1iBink+1i
而伯努利 B i B_i Bi 可以通过递推得到
B m = − 1 m + 1 ∑ i = 0 m − 1 C m + 1 i B i B_m=-\frac{1}{m+1}\sum_{i=0}^{m-1} C_{m+1}^i B_i Bm=m+11i=0m1Cm+1iBi
代入 B 0 = 1 B_0=1 B0=1 ,就可以得到一系列伯努利数了

算法过程
  1. 代入公式,把系数用分数求出来
  2. 再把这些分数形式的系数乘上他们分母的公倍数,就能让系数都为整数,并且 M M M 最小。

代码

#include <stdio.h>
#include <string.h>

long long gcd(long long a, long long b) {
    if (!b) return a;
    return gcd(b, a % b);
}

long long lcm(long long a, long long b) {
    a = a / gcd(a, b) * b;
    if (a < 0) a = -a;
    return a;
}

struct Fraction {
    long long a, b;
    Fraction() {a = 0; b = 1;}

    Fraction(long long x) {
        a = x; b = 1;
    }

    Fraction(long long x, long long y) {
        a = x; b = y;
    }

    void deal() {
        if (b < 0) {b = -b; a = -a;}
        long long k = gcd(a, b);
        if (k < 0) k = -k;
        a /= k; b /= k;
    }

    Fraction operator+(Fraction p) {
        Fraction ans;
        ans.b = lcm(b, p.b);
        ans.a = ans.b / b * a + ans.b / p.b * p.a;
        ans.deal();
        return ans;
    }

    Fraction operator-(Fraction p) {
        Fraction ans;
        ans.b = lcm(b, p.b);
        ans.a = ans.b / b * a - ans.b / p.b * p.a;
        ans.deal();
        return ans;
    }

    Fraction operator*(Fraction p) {
        Fraction ans;
        ans.a = a * p.a;
        ans.b = b * p.b;
        ans.deal();
        return ans;
    }

    Fraction operator/(Fraction p) {
        Fraction ans;
        ans.a = a * p.b;
        ans.b = b * p.a;
        ans.deal();
        return ans;
    }

    void operator=(int x) {
        a = x;
        b = 1;
    }

    void print() {
        printf("%lld/%lld\n", a, b);
    }
};

const int N = 25;

Fraction B[N], C[N][N], a[N];
long long L;

void init() {
    for (int i = 0; i < N; i++) {
        C[i][0] = 1;
        C[i][i] = 1;
        for (int j = 1; j < i; j++) {
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
        }
    }

    B[0] = 1;
    for (int i = 1; i <= 20; i++) {
        B[i] = 0;
        for (int j = 0; j < i; j++) B[i] = B[i] - C[i + 1][j] * B[j];
        B[i] = B[i] / (i+1);
    }
}

int main() {
//    freopen("in.text","r",stdin);

    int cas,k;
    init();
    scanf("%d", &cas);
    while (cas--) {
        L = 1;
        scanf("%d", &k);
        for (int i = 0; i <= k; i++){
            a[i] = C[k+ 1][i] * B[i] * Fraction(1, k+ 1);
            L = lcm(L, a[i].b);
        }
        printf("%lld ", L);
        a[1] = a[1] + 1;
        for (int i = 0; i <=k; i++)printf("%lld ", L / a[i].b * a[i].a);
        printf("0\n");
        if (cas) printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是Mally呀!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值