【51nod】序列求和v1~v5解题报告

  • 本文为博主原创,未经许可不得转载

今天考了机房最强妹子wyj的互测题,跪在了T2

然后uoj大佬说这是一道51nod上的题,于是我便去创建了一个账号

然后发现51nod上有5道这样的题目

于是我就立下了一个flag,然将这5道题目切掉

(flag毕竟是flag,有很大的可能性刚不动而弃疗

序列求和$level 1$

传送门

【一句话题意】

给定$n$和$k$,求$\sum_{i=1}^{n}i^k$,结果对$10^9+7$取模

【解析】

有一个叫做伯努利数的东西,它是这样定义的:$\sum_{k=0}^{n} C_{n+1}^kB_{k}=0$
  
通过定义我们可以得到它的递推式:$b_{n}=-\frac{1}{n+1}(C_{n+1}^0B_{0}+C_{n+1}^1B_{1}+…+C_{n+1}^{n-1} B_{n-1})$
  
然后给出自然数幂求和的公式(我不会证明):$\sum_{i=1}^{n}i^k=\frac{1}{k+1}\sum_{i=1}^{k+1} C_{k+1}^i B_{k+1-i} (n+1)^i$
  
那么这个问题就完美的解决了,我们只需要递推出所有的伯努利数,就能求出自然数幂的和
  
时间复杂度:$O(n^2)$

【AC代码】(话说这东西直接在51nod上看还得花60个点头盾)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<bits/stdc++.h>
#define FILE "read"
#define MAXN 2000
#define D 50
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7;
ll n,k,ans,p[MAXN+D],b[MAXN+D],inv[MAXN+D],c[MAXN+D][MAXN+D];
inline ll read(){
ll x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
void pre(){
for(int i=0;i<=MAXN;++i){
c[i][0]=c[i][i]=1;
for(int j=1;j<i;++j)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
inv[1]=1;for(int i=2;i<=MAXN;++i)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
b[0]=1;
for(int i=1;i<=MAXN;++i){
for(int j=0;j<i;++j) (b[i]+=b[j]*c[i+1][j]%mod)%=mod;
b[i]*=-inv[i+1]; b[i]=(b[i]%mod+mod)%mod;
}
}
void solve(){
n=read(); k=read(); n%=mod; p[0]=1; ans=0;
for(int i=1;i<=k+1;++i) p[i]=p[i-1]*(n+1)%mod;
for(int i=1;i<=k+1;++i) (ans+=c[k+1][i]*b[k+1-i]%mod*p[i]%mod)%=mod;
ans=ans*inv[k+1]%mod;
printf("%lld\n",ans);
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
pre();for(int T=read();T;--T)solve();
return 0;
}

  

  

  

序列求和$level2$

传送门

【一句话题意】

给定$n$,$k$,$r$,求$\sum_{i=1}^{n}i^k r^i$,结果对$10^9+7$取模

【解析】

错位相减法 (似乎是高中数学知识)
  
设$S(n,k)=\sum_{i=1}^{n}i^k r^i$,那么有:
  
$S(n,k)=1^k r^1+2^k r^2+…+n^k r^n$
  
$rS(n,k)=1^k r^2+2^k r^3+…+n^k r^{n+1}$
  
$(1-r)S(n,k)=r+(2^k-1^k)r^2+(3^k-2^k)r^3+…+[(n+1)^k-n^k)]r^{n+1}-(n+1)^k r^{n+1}$
  
重点关注$(n+1)^k-n^k$,因为我们发现$(n+1)^k$可以使用二项式定理展开
  
$(n+1)^k-n^k=C_{k}^1 n^{k-1}+C_{k}^2 n^{k-2}+…+C_{k}^k n^0$
  
所以$S(n,k)=\sum_{i=1}^{k} \sum_{j=1}^{n}C_{k}^i j^{k-i}r^j+r-(n+1)^k r^{n+1}$
  
所以$S(n,k)=\sum_{i=1}^{k}C_{k}^{i} S(n,k-i)+r-(n+1)^k r^{n+1}$
  
这是一个递归式,当$k=0$时,$S(n,0)=\frac{r(r^n-1)}{r-1}$
  
所以我们使用记忆化搜索就能解决这个问题
  
注意当$r=1$时,分母为$0$,此时需要特判,解法和$level1$是一样的

【AC代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
#define FILE "read"
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7,MAXN=2000,D=50;
ll n,k,r,c[MAXN+D][MAXN+D],inv[MAXN+D],b[MAXN+D],f[MAXN+D],p[MAXN+D];
inline ll read(){
ll x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
ll fast(ll a,ll b){ll temp(1);for(;b;b>>=1,a=a*a%mod)if(b&1)temp=temp*a%mod;return temp;}
void pre(){
for(int i=0;i<=MAXN;++i){
c[i][0]=c[i][i]=1;
for(int j=0;j<i;++j)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
inv[1]=1; for(int i=2;i<=MAXN;++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
b[0]=1; for(int i=1;i<=MAXN;++i){
for(int j=0;j<i;++j) (b[i]+=c[i+1][j]*b[j]%mod)%=mod;
b[i]*=-inv[i+1]; b[i]=(b[i]%mod+mod)%mod;
}
}
ll dfs(int k){
if(f[k]!=-1) return f[k];
if(!k){
f[k]=r*(fast(r,n)-1)%mod*fast(r-1,mod-2)%mod;
f[k]=(f[k]%mod+mod)%mod;
return f[k];
}
f[k]=fast((n+1)%mod,k)*fast(r,n+1)%mod*fast(r-1,mod-2)%mod; ll temp=1;
for(int i=1;i<=k;++i) (temp+=c[k][i]*dfs(k-i)%mod)%=mod;
f[k]=f[k]-r*fast(r-1,mod-2)%mod*temp%mod; f[k]=(f[k]%mod+mod)%mod;
return f[k];
}
void solve(){
n=read(); k=read(); r=read()%mod;
memset(f,-1,sizeof(f));
if(r==1){
ll ans=0; n%=mod; p[0]=1;
for(int i=1;i<=k+1;++i) p[i]=p[i-1]*(n+1)%mod;
for(int i=1;i<=k+1;++i) ans=(ans+c[k+1][i]*b[k+1-i]%mod*p[i]%mod)%mod;
ans=ans*inv[k+1]%mod;
printf("%lld\n",ans);
}
else printf("%lld\n",dfs(k));
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
pre();for(int T=read();T;--T)solve();
return 0;
}

  

  

  

序列求和$level3$

传送门

【一句话题意】

给定$n$和$k$,求$\sum_{i=1}^{n} f[i]^k$,其中$f[i]$为斐波那契数列第$i$项,结果对$10^9+9$取模

【解析】

首先我们知道菲波那切数列的通项为$f[n]=\frac{1}{\sqrt 5}[(\frac{1+\sqrt 5}{2})^n-(\frac{1-\sqrt 5}{2})^n]$
  
所以$ans=\sum_{i=1}^{n}(\frac{1}{\sqrt 5})^k [(\frac{1+\sqrt 5}{2})^i-(\frac{1-\sqrt 5}{2})^i]^k$
  
根据二项式定理展开得到:$ans=(\frac{1}{\sqrt 5})^k \sum_{i=1}^{n} \sum_{j=0}^{k} (-1)^j C_{k}^{j} [(\frac{1+\sqrt 5}{2})^{k-j}-(\frac{1-\sqrt 5}{2})^j]^i$
  
变换求和号位置得到:$ans=(\frac{1}{\sqrt 5})^k \sum_{i=0}^{k} (-1)^i C_{k}^{i} \sum_{j=1}^{n} [(\frac{1+\sqrt 5}{2})^{k-i}-(\frac{1-\sqrt 5}{2})^i]^j$
  
然后我们发现$\sum_{j=1}^{n} [(\frac{1+\sqrt 5}{2})^{k-i}-(\frac{1-\sqrt 5}{2})^i]^j$相当于等比数列求和,直接计算就好了
  
时间复杂度:$O(k\log n)$
  
注意:求$\sqrt 5$在模意义下的值需要使用二次剩余,即计算$x^2 = 5 (mod p)$的方程的解

【AC代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
#define FILE "read"
#define MAXN 100000
#define D 50
using namespace std;
typedef long long ll;
const ll mod=(ll)1e9+9;
ll n,k,B,w,A[5],p[MAXN+D],inv[MAXN+D];
inline ll read(){
ll x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
struct node{
ll p,d;
node(ll a=0,ll b=0):p(a),d(b){}
inline node operator *(const node &b){
return node((p*b.p%mod+d*b.d%mod*w%mod)%mod,(p*b.d%mod+d*b.p%mod)%mod);
}
};
ll fast(ll a,ll b){ll temp(1);for(;b;b>>=1,a=a*a%mod)if(b&1)temp=temp*a%mod;return temp;}
node Fast(node a,ll b){node temp(1,0);for(;b;b>>=1,a=a*a)if(b&1)temp=temp*a;return temp;}
ll C(ll a,ll b){return p[a]*inv[b]%mod*inv[a-b]%mod;}
ll Lerangde(ll x){return fast(x,(mod-1)>>1);}
ll solve(ll n){
while(1){
ll a=rand()%mod; w=((a*a-n)%mod+mod)%mod;
if(Lerangde(w)==mod-1){
node temp(a,1); temp=Fast(temp,(mod+1)>>1);
return temp.p;
}
}
}
void pre(){
B=solve(5); p[0]=1;
A[1]=(1+B)*fast(2,mod-2)%mod;
A[2]=(1-B)*fast(2,mod-2)%mod;
for(int i=1;i<=MAXN;++i) p[i]=p[i-1]*i%mod;
inv[MAXN]=fast(p[MAXN],mod-2);
for(int i=MAXN-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
void work(){
n=read(); k=read(); ll ans=0;
for(int i=0;i<=k;++i){
ll q=fast(A[1],k-i)*fast(A[2],i)%mod;
if(q==1){
ans+=C(k,i)*fast(-1,i)%mod*(n%mod)%mod;
ans=(ans%mod+mod)%mod;
}
else{
ans+=C(k,i)*fast(-1,i)%mod*q%mod*(fast(q,n)-1)%mod*fast(q-1,mod-2)%mod;
ans=(ans%mod+mod)%mod;
}
}
ans=ans*fast(fast(B,k),mod-2)%mod;
printf("%lld\n",ans);
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
pre();for(ll T=read();T;--T)work();
return 0;
}

  

  

  

序列求和$level4$

传送门

【一句话题意】

给定$n$和$k$,求$\sum_{i=1}^{n}i^k$,结果对$10^9+7$取模
  
$level4$是$level1$的加强版

【解析】

$k$的范围到了$50000$,所以我们不能用$level1$中的方法预处理伯努利数了,要想办法把$N^2$的复杂度降下来
  
伯努利数的生成函数是$G(x)=\sum_{i=0}^{+oo}\frac{B_i}{i!} x_i$
  
其中$G(x)$的计算式为$G(x)=\frac{x}{e^x-1}$
  
将$e^x$进行泰勒展开得到$G(x)=\frac{1}{\sum_{i=0}^{+oo} \frac{x^i}{(i+1)!}}$
  
所以我们只需要求多项式$\frac{1}{\sum_{i=0}^{+oo} \frac{x^i}{(i+1)!}}$的逆元就能求出$G(x)$,从而预处理出伯努利数
  
时间复杂度:$O(n\log n)$
  
注意:多项式逆元需要用到$NTT$,但是模数不符合要求,所以我们采用三模数$NTT$再用$CRT$还原系数
  
写起来非常糟心,博主调了整整三天才A掉,千万注意$CRT$还原系数时必须一步一步还原
  
具体参考代码,不明白的地方可以留言

【AC代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include<bits/stdc++.h>
#define FILE "read"
#define MAXN 264000
using namespace std;
typedef long long ll;
const int M[3]={998244353,1004535809,469762049};
const int G[3]={3,3,3};
const int mod=1e9+7;
const ll MOD=(ll)M[0]*M[1];
ll fac[MAXN],inv[MAXN],a[MAXN],b[MAXN],c[MAXN],temp[MAXN],p[MAXN],B[MAXN],_b[3][MAXN];
inline ll read(){
ll x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
struct node{
ll P,G,w[MAXN],R[MAXN],wn[2][MAXN];
inline void swap(ll &a,ll &b){a=a^b;b=a^b;a=a^b;}
inline ll add(ll a,ll b){return (a+=b)>=P?a-P:a;}
inline ll sub(ll a,ll b){return (a-=b)<0?a+P:a;}
inline ll mul(ll a,ll b){return 1LL*a*b%P;}
inline ll fast(ll a,ll b){ll sum(1);for(;b;b>>=1,a=mul(a,a))if(b&1)sum=mul(sum,a);return sum;}
void pre(ll p,ll g){
P=p; G=g; w[0]=1;
wn[0][18]=fast(G,(P-1)/(1<<18));
wn[1][18]=fast(wn[0][18],P-2);
for(int i=17;i>=0;--i)for(ll k=0;k<=1;++k)wn[k][i]=mul(wn[k][i+1],wn[k][i+1]);
}
void NTT(ll *a,ll H,ll f=0){
for(int i=0;i<(1<<H);++i) if(i<(R[i]=(R[i>>1]>>1)|((i&1)<<(H-1)))) swap(a[i],a[R[i]]);
for(int e=1;e<=H;++e){
int l=1<<(e-1);
for(int i=1;i<l;++i) w[i]=mul(w[i-1],wn[f][e]);
for(int st=0;st<(1<<H);st+=l<<1) for(int k=0;k<l;++k){
ll x=a[st+k],y=mul(w[k],a[st+k+l]);
a[st+k]=add(x,y); a[st+k+l]=sub(x,y);
}
}
if(f) for(int i=0;i<(1<<H);++i) a[i]=mul(a[i],fast((1<<H),P-2));
}
}ntt[3];
ll C(ll n,ll m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline ll mul(ll x,ll y,ll p){return ((x*y-(ll)(((long double)x*y+0.5)/p)*p)%p+p)%p;}//一行快速乘
inline ll fast(ll a,ll b,ll p){ll ret(1);a%=p;for(;b;b>>=1,a=a*a%p)if(b&1)ret=ret*a%p;return ret;}
inline ll CRT(ll a1,ll a2,ll a3){
ll A=(mul(a1*M[1]%MOD,fast(M[1],M[0]-2,M[0]),MOD)+mul(a2*M[0]%MOD,fast(M[0],M[1]-2,M[1]),MOD))%MOD;
ll k=(a3+M[2]-A%M[2])*fast(MOD,M[2]-2,M[2])%M[2];
return (k*(MOD%mod)%mod+A)%mod;
}
void getinv(ll *a,int L){
for(int i=0;i<3;++i) ntt[i].pre(M[i],G[i]);
b[0]=CRT(ntt[0].fast(a[0],M[0]-2),ntt[1].fast(a[0],M[1]-2),ntt[2].fast(a[0],M[2]-2));
for(int i=0;i<3;++i) _b[i][0]=ntt[i].fast(a[0],M[i]-2);
for(int now=0;(1<<now)<L;++now){
int l=1<<(now+1);
for(int k=0;k<3;++k){
for(int i=0;i<l;++i) temp[i]=a[i],temp[i+l]=0;
for(int i=0;i<l;++i) _b[k][i]=b[i],_b[k][i+l]=0;
ntt[k].NTT(_b[k],now+2); ntt[k].NTT(temp,now+2);
for(int i=0;i<(l<<1);++i) _b[k][i]=ntt[k].mul(temp[i],_b[k][i]);
ntt[k].NTT(_b[k],now+2,1);
}
for(int i=0;i<l;++i) c[i]=CRT(_b[0][i],_b[1][i],_b[2][i]);
for(int i=0;i<l;++i) c[i]=(c[i]==0?0:mod-c[i]); c[0]+=2;
for(int k=0;k<3;++k){
for(int i=0;i<l;++i) temp[i]=c[i],temp[i+l]=0;
for(int i=0;i<l;++i) _b[k][i]=b[i],_b[k][i+l]=0;
ntt[k].NTT(_b[k],now+2); ntt[k].NTT(temp,now+2);
for(int i=0;i<(l<<1);++i) _b[k][i]=ntt[k].mul(temp[i],_b[k][i]);
ntt[k].NTT(_b[k],now+2,1);
}
for(int i=0;i<l;++i) b[i]=CRT(_b[0][i],_b[1][i],_b[2][i]);
}
}
void pre(){
int H,L; for(H=0,L=1;L<=50000;L<<=1,++H);
fac[0]=1; for(int i=1;i<=L;++i) fac[i]=fac[i-1]*i%mod;
inv[1]=1; for(int i=2;i<=L;++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
inv[0]=1; for(int i=1;i<=L;++i) inv[i]=inv[i-1]*inv[i]%mod;
for(int i=0;i<L;++i) a[i]=inv[i+1]; getinv(a,L);
for(int i=0;i<L;++i) B[i]=b[i]*fac[i]%mod;
}
void solve(){
ll n=read()%mod,k=read(),ans=0; p[0]=1;
for(int i=1;i<=k+1;++i) p[i]=p[i-1]*(n+1)%mod;
for(int i=1;i<=k+1;++i){
ans+=C(k+1,i)*B[k+1-i]%mod*p[i]%mod;
ans=(ans%mod+mod)%mod;
}
ans=ans*fast(k+1,mod-2,mod)%mod;
printf("%lld\n",ans);
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
pre();for(int T=read();T;--T) solve();
return 0;
}
文章目录
  1. 1. 序列求和$level 1$
  2. 2. 序列求和$level2$
  3. 3. 序列求和$level3$
  4. 4. 序列求和$level4$
,