伯努利数与自然数幂和
今天我們討論的問題是如何有效地求自然數的冪和。接下來以3個經典題目為例來講解。
?
題目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1864
?
分析:其實求自然數的冪和方法有很多種,先來看看普通的遞推求法,由于
?
????
?
???? 那么對于所有的累加得到
?
????
?
???? 進一步得到
?
????
?
???? 可以看出這是一個遞推式,如果我們記
?
??? ??
?
?????那么得到如下遞歸式
?
?????
?
?????遞歸出口是
?
?????
?
?????為了提高效率,在遞歸的時候需要記憶化。由于要用到大數,用Java實現。
?
代碼:
import java.math.*; import java.util.*;public class Main {public static final int N = 105;public static final BigInteger FLAG = (BigInteger.ZERO).subtract(BigInteger.ONE);public static BigInteger[][] C = new BigInteger[N][N];public static BigInteger[] ans = new BigInteger[N];public static void Init(){for(int i=0; i<N; i++){C[i][0] = C[i][i] = BigInteger.ONE;if(i == 0) continue;for(int j=1; j<i; j++)C[i][j] = C[i-1][j].add(C[i-1][j-1]);}}public static BigInteger Solve(BigInteger n, int k){if(ans[k].compareTo(FLAG) != 0){return ans[k];}if(k == 1){ans[k] = ((n.add(BigInteger.ONE)).multiply(n)).divide(BigInteger.valueOf(2));return ans[k];}BigInteger tmp = BigInteger.ONE;for(int i=0; i<k+1; i++){tmp = tmp.multiply(n.add(BigInteger.ONE));}tmp = tmp.subtract(n.add(BigInteger.ONE));BigInteger sum = BigInteger.ZERO;for(int i=1; i<k; i++){BigInteger t = C[k+1][i+1].multiply(Solve(n, k-i));sum = sum.add(t);}ans[k] = (tmp.subtract(sum)).divide(BigInteger.valueOf(k+1));return ans[k];}public static void main(String[] args){Init();Scanner cin = new Scanner(System.in);while(cin.hasNext()){BigInteger n = cin.nextBigInteger();int k = cin.nextInt();for(int i=0; i<N; i++){ans[i] = FLAG;}System.out.println(Solve(n, k));}} }
?
題目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1228
?
分析:本題題意就是求自然數的冪和,但是它的case比較多。對于求冪和本身就需要的時間復雜度,如果繼
???? 續用上述方法來求自然數的冪和,5000個case會TLE,接下來介紹另一個求自然數冪和的方法,它是基于伯
?????努利數的,公式描述如下
?
????
?
?????可以看出只要我們預處理出每一項,就可以在線性時間內求得自然數的冪和。前面的倒數可以用遞推法求逆元
???? 預處理,組合數也可以預處理,也可以先預處理,現在關鍵是如何預處理伯努利數。
?
?????伯努利數滿足條件,且有
?
????
?
?????那么繼續得到
?
????
?
?????這就是伯努利數的遞推式,逆元部分同樣可以預處理。
?
代碼:
#include <iostream> #include <string.h> #include <stdio.h>using namespace std; typedef long long LL; const LL MOD = 1000000007; const int N = 2005;LL C[N][N]; LL B[N],Inv[N]; LL Tmp[N]; LL n;void Init() {//預處理組合數for(int i=0; i<N; i++){C[i][0] = C[i][i] = 1;if(i == 0) continue;for(int j=1; j<i; j++)C[i][j] = (C[i-1][j] % MOD + C[i-1][j-1] % MOD) % MOD;}//預處理逆元Inv[1] = 1;for(int i=2; i<N; i++)Inv[i] = (MOD - MOD / i) * Inv[MOD % i] % MOD;//預處理伯努利數B[0] = 1;for(int i=1; i<N; i++){LL ans = 0;if(i == N - 1) break;for(int j=0; j<i; j++){ans += C[i+1][j] * B[j];ans %= MOD;}ans *= -Inv[i+1];ans = (ans % MOD + MOD) % MOD;B[i] = ans;} }LL Work(int k) {LL ans = Inv[k+1];LL sum = 0;for(int i=1; i<=k+1; i++){sum += C[k+1][i] * Tmp[i] % MOD * B[k+1-i] % MOD;sum %= MOD;}ans *= sum;ans %= MOD;return ans; }int main() {int T;Init();scanf("%d", &T);while(T--){int k;scanf("%I64d %d", &n, &k);n %= MOD;Tmp[0] = 1;for(int i=1; i<N; i++)Tmp[i] = Tmp[i-1] * (n + 1) % MOD;printf("%I64d\n", Work(k));}return 0; }
題目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1258
?
分析:本題與上題不同的是值比較大,達到50000,如果采用同樣的方法,會TLE的。那么必定要進行優化。
?
???? 參考Pick大神的方法,如下
?
????
?
???? 鏈接:http://picks.logdown.com/posts/189620-the-inverse-element-of-polynomial
?
?
總結
以上是生活随笔為你收集整理的伯努利数与自然数幂和的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 优先队列的应用
- 下一篇: Hash冲突处理终极版