[51nod1228][伯努利数][自然数k次幂和]序列求和

7 篇文章 0 订阅
2 篇文章 0 订阅
题意

给定n,k,求 ni=1ik


因为绍一模拟考的20分部分分,所以来学一发伯努利数

首先暴力求这个式子是要nlogk的,然而n是10^18……

然后了解到一个叫伯努利数的东西,这个k^2次预处理后O(k)求出解

伯努利数是一个乱七八糟的有理数数列,

定义 B0=1

根据关系式 ni=0Cin+1Bi=0 可以得到递推式

Bn=1n+1n1i=0Cin+1Bi

这样就可以O(k^2)预处理出伯努利数

因为又知道一个神奇的公式

ni=1ik=1k+1k+1i=1Cik+1Bk+1i(n+1)i

那么就可以O(k)求出自然数k次幂和啦

学习自http://blog.csdn.net/acdreamers/article/details/38929067

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 2010
#define p 1000000007

using namespace std;

typedef long long ll;

int t;
ll n,k;
ll inv[N],fac[N],invf[N],B[N];

inline ll C(ll x,ll y){
  return fac[x]*invf[y]%p*invf[x-y]%p;
}

inline void reaD(int &x){
  char c=getchar();x=0;
  for(;c>57||c<48;c=getchar());for(;c>=48&&c<=57;x=x*10+c-48,c=getchar());
}

inline void reaD(ll &x){
  char c=getchar();x=0;
  for(;c>57||c<48;c=getchar());for(;c>=48&&c<=57;x=x*10+c-48,c=getchar());
}

ll pow_(ll x,ll y){
  x%=p;
  ll r=1;
  while(y){
    if(y&1) r=r*x%p;
    x=x*x%p;
    y>>=1;
  }
  return r;
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  invf[0]=inv[0]=inv[1]=fac[0]=fac[1]=1;
  for(ll i=1;i<=2005;i++) fac[i]=fac[i-1]*i%p;
  for(int i=2;i<=2005;i++) inv[i]=(p-p/i)*inv[p%i]%p;
  for(int i=1;i<=2005;i++) invf[i]=invf[i-1]*inv[i]%p;
  B[0]=1;
  for(int i=1;i<=2001;i++){
    B[i]=0;
    for(int j=0;j<i;j++) B[i]=(B[i]+C(i+1,j)*B[j])%p;
    B[i]=((B[i]*-inv[i+1])%p+p)%p;
  }
  reaD(t);
  while(t--){
    reaD(n); reaD(k);
    ll Ans=0;
    for(int i=1;i<=k+1;i++) Ans=(Ans+C(k+1,i)*B[k+1-i]%p*pow_(n+1,i))%p;
    Ans=Ans*inv[k+1]%p;
    printf("%lld\n",Ans);
  }
  return 0;
}

查了下维基百科

伯努利数有两类,两类伯努利数的区别在于 B1 的正负

第一类伯努利数,由美国国家标准技术研究所 (NIST)制定,在这标准下 B1=12

第二类伯努利数,又被称为是“原始的白努利数“,在这标准下 B1=12

上面的求自然数k次幂和是根据第一类伯努利数得出,可以看出这个做法得到的多项式是关于(n+1)的,那么在某些题目中,要求多项式关于n,这时候就需要用第二类伯努利数。

第二类伯努利数求自然数k次幂和:

ni=1ik=1k+1ki=0Cik+1Bink+1i

很神奇啊

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 2010
#define p 1000000007

using namespace std;

typedef long long ll;

int t;
ll n,k;
ll inv[N],fac[N],invf[N],B[N];

inline ll C(ll x,ll y){
  return fac[x]*invf[y]%p*invf[x-y]%p;
}

inline void reaD(int &x){
  char c=getchar();x=0;
  for(;c>57||c<48;c=getchar());for(;c>=48&&c<=57;x=x*10+c-48,c=getchar());
}

inline void reaD(ll &x){
  char c=getchar();x=0;
  for(;c>57||c<48;c=getchar());for(;c>=48&&c<=57;x=x*10+c-48,c=getchar());
}

ll pow_(ll x,ll y){
  x%=p;
  ll r=1;
  while(y){
    if(y&1) r=r*x%p;
    x=x*x%p;
    y>>=1;
  }
  return r;
}

int main(){
  invf[0]=inv[0]=inv[1]=fac[0]=fac[1]=1;
  for(ll i=1;i<=2005;i++) fac[i]=fac[i-1]*i%p;
  for(int i=2;i<=2005;i++) inv[i]=(p-p/i)*inv[p%i]%p;
  for(int i=1;i<=2005;i++) invf[i]=invf[i-1]*inv[i]%p;
  B[0]=1;
  for(int i=1;i<=2001;i++){
    B[i]=0;
    for(int j=0;j<i;j++) B[i]=(B[i]+C(i+1,j)*B[j])%p;
    B[i]=((B[i]*-inv[i+1])%p+p)%p;
  }
  B[1]=(p-B[1])%p;
  reaD(t);
  while(t--){
    reaD(n); reaD(k);
    ll Ans=0;
    for(int i=0;i<=k;i++) Ans=(Ans+C(k+1,i)*B[i]%p*pow_(n,k+1-i))%p;
    Ans=Ans*inv[k+1]%p;
    printf("%lld\n",Ans);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值