洛谷P5364 [SNOI2017]礼物 题解

2020/02/11 upd

更换markdown编辑器,修了一下写错了的式子


 

传送门

题目描述

热情好客的小猴子请森林中的朋友们吃饭,他的朋友被编号为 1?N,每个到来的朋友都会带给他一些礼物:大香蕉。其中,第一个朋友会带给他 1 个大香蕉,之后,每一个朋友到来以后,都会带给他之前所有人带来的礼物个数再加他的编号的K次方那么多个。所以,假设K=2,前几位朋友带来的礼物个数分别是:

1,5,15,37,83,…

假设K=3,前几位朋友带来的礼物个数分别是:

1,9,37,111,…

现在,小猴子好奇自己到底能收到第 N 个朋友多少礼物,因此拜托于你了。

已知 N,K,请输出第 N 个朋友送的礼物个数 mod 10^9+7。

输入格式

第一行,两个整数 N,K。

输出格式

一个整数,表示第 N 个朋友送的礼物个数 mod 10^9+7 。

输入输出样例

输入 #1

4 2

输出 #1

37

输入 #2

2333333 2

输出 #2

514898185

输入 #3

1234567890000 3

输出 #3

891659731

输入 #4

66666666 10

输出 #4

32306309

100% 的数据:N1018,K10

暴搞通项公式

蒟蒻想了一上午弄出来个O(k2)的算法

这道题比较裸,就是甩给你个递推式让你求第n

A1=1,An=i=1n1Ai+nk

那首先我们来手动打个表qwq

Ai \ ik 1k 2k 3k 4k 5k 6k
A1 1
A2 1 1
A3 2 1 1
A4 4 2 1 1
A5 8 4 2 1 1
A6 16 8 4 2 1 1

表中第i行的系数乘上对应列标后的和就是Ai

于是我们发现了这一显然的规律

A1=1,An=2An1+nk(n1)k

我们就非常优秀的把这个递推式化简了:p

2020/05/16 update

当时太菜了没学数列,实际上这个规律根本就不需要找,可以直接推出。

Sn=i=1nan ,根据题意 an=Sn1+nk

用上式减去 an1=Sn2+(n+1)k

anan1=an1+nk(n+1)kan=2an1+nk(n+1)k

得到上式。

总感觉它有个通项公式什么的吧,我们来胡乱瞎推一波

观察递推式,右式那坨nk(n1)k看着就恶心,我们想找个办法把它消掉,使它的形式变成一个等比数列,这样通项公式就容易得到了

显然nk(n1)k是一个k1次多项式,所以我们构造数列Uk1次多项式B

Un=An+B(n)B(n)=i=0k1bini

对数列U的定义式移项得

An=UnB(n)

带回A的递推式,得

UnB(n)=2(Un1B(n1))+nk(n1)kUn=2Un1+B(n)2B(n1)+nk(n1)k

我们想让Un=2Un1,只需使

B(n)2B(n1)+nk(n1)k=0

B(n)+2B(n1)=nk(n1)k

现在我们要求解多项式B,试着将多项式的每一项,也就是bi,都表示出来

先看右式,用二项式定理展开(n1)k,右式变为

nki=0kCki(1)kini

提出和式中的k次项与nk消掉

=i=0k1Cki(1)kini

再来看左式,将多项式展开得

i=0k1bini+2i=0k1bi(n1)i

也用二项式定理展开(n1)i

=i=0k1bini+2i=0k1bij=0iCij(1)ijnj

转换枚举

=i=0k1bini+2i=0k1j=0ibiCij(1)ijnj=i=0k1bini+2j=0k1i=jk1biCij(1)ijnj=i=0k1bini+2i=0k1{j=ik1bjCji(1)ji}ni

(这里大括号只是为了标明系数,没有实际意义)

现在把左右式合在一起写

i=0k1bini+2i=0k1{j=ik1bjCji(1)ji}ni=i=0k1Cki(1)kini

消掉负号

i=0k1bini+2i=0k1{j=ik1bjCji(1)ji}ni=i=0k1Cki(1)kini

所以

bi+2j=ik1bjCji(1)ji=Cki(1)ki

于是我们非常愉快艰难的得到了bi的表示,高斯消元即可得到bi

仔细观察发现这是个上三角矩阵,所以我们可以直接O(k2)求解!

于是我们解出了多项式B

回过头来看数列U的定义,Un=An+B(n)

现在解出了B,我们又知道A1=1,就能知道

U1=A1+B(1)=B(1)+1

于是我们得到了数列U的完整递推式

U1=B(1)+1,Un=2Un1

现在就容易知道U的通项公式了,它是

Un=(B(1)+1)2n1

又因为An=UnB(n)A的通项公式就出来了!

An=(B(1)+1)2n1B(n)

完了

//洛谷P5364 [SNOI2017]礼物
//Author:sun123zxy
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<queue>
using namespace std;
typedef long long ll;
const ll MOD=1E9+7;
ll QPow(ll x,ll up){//快速幂
x%=MOD;
ll ans=1;
while(up){
if(up%2==0){
x=x*x%MOD;
up/=2;
}else{
ans=ans*x%MOD;
up--;
}
}
return ans;
}
ll Inv(ll x){//逆元
return QPow(x,MOD-2);
}
const ll MXK=2005;
ll fac[MXK],facInv[MXK];
void FacInit(ll n){
fac[0]=1;for(ll i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;//求阶乘
facInv[n]=Inv(fac[n]);
for(ll i=n-1;i>=1;i--) facInv[i]=facInv[i+1]*(i+1)%MOD;//线性求阶乘逆元
facInv[0]=1;
}
ll C(ll n,ll k){//组合数
if(n<k) return 0;
return fac[n]*facInv[n-k]%MOD*facInv[k]%MOD;
}
ll N,K;
ll c,B[MXK];//2^(n-1)的系数c和多项式B
ll GetY(ll x){//获取B(x)
x%=MOD;
ll y=0;
ll xPow=1;
for(int i=0;i<=K-1;i++){
y=(y+B[i]*xPow)%MOD;
xPow=xPow*x%MOD;
}
return y;
}
ll mtx[MXK][MXK];
void GetFormula(){
for(ll i=0;i<=K-1;i++) for(ll j=0;j<=K;j++) mtx[i][j]=0;
for(ll i=0;i<=K-1;i++){//初始化方程组
mtx[i][i]=1;
for(ll j=i;j<=K-1;j++){
ll p=-1;if((j-i)%2==0) p=1;
mtx[i][j]+=(-2*C(j,i)%MOD*p+MOD)%MOD;
}
ll p=-1;if((K-i)%2==0) p=1;
mtx[i][K]=(C(K,i)*p+MOD)%MOD;
}
for(ll i=K-1;i>=0;i--){//上三角高斯消元
B[i]=mtx[i][K]*Inv(mtx[i][i])%MOD;
for(ll j=i-1;j>=0;j--){
mtx[j][K]=(mtx[j][K]-B[i]*mtx[j][i]%MOD+MOD)%MOD;
mtx[j][i]=0;
}
}
c=(GetY(1)+1)+MOD%MOD;
}
int main(){
cin>>N>>K;
FacInit(K);
GetFormula();
cout<<(c*QPow(2,N-1)%MOD-GetY(N)+MOD)%MOD;
return 0;
}

和洛谷题解里rqy聚聚的解法似乎有一些关联(

2019/07/01

posted @   sun123zxy  阅读(466)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示