洛谷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% 的数据:
暴搞通项公式
蒟蒻想了一上午弄出来个的算法
这道题比较裸,就是甩给你个递推式让你求第项
那首先我们来手动打个表qwq
\ | ||||||
---|---|---|---|---|---|---|
1 | ||||||
1 | 1 | |||||
2 | 1 | 1 | ||||
4 | 2 | 1 | 1 | |||
8 | 4 | 2 | 1 | 1 | ||
16 | 8 | 4 | 2 | 1 | 1 |
表中第行的系数乘上对应列标后的和就是
于是我们发现了这一显然的规律
我们就非常优秀的把这个递推式化简了:p
2020/05/16 update
当时太菜了没学数列,实际上这个规律根本就不需要找,可以直接推出。
设 ,根据题意 。
用上式减去 得
得到上式。
总感觉它有个通项公式什么的吧,我们来胡乱瞎推一波
观察递推式,右式那坨看着就恶心,我们想找个办法把它消掉,使它的形式变成一个等比数列,这样通项公式就容易得到了
显然是一个次多项式,所以我们构造数列和次多项式
对数列的定义式移项得
带回的递推式,得
我们想让,只需使
即
现在我们要求解多项式,试着将多项式的每一项,也就是,都表示出来
先看右式,用二项式定理展开,右式变为
提出和式中的次项与消掉
再来看左式,将多项式展开得
也用二项式定理展开
转换枚举
(这里大括号只是为了标明系数,没有实际意义)
现在把左右式合在一起写
消掉负号
所以
于是我们非常愉快艰难的得到了的表示,高斯消元即可得到。
仔细观察发现这是个上三角矩阵,所以我们可以直接求解!
于是我们解出了多项式。
回过头来看数列的定义,
现在解出了,我们又知道,就能知道
于是我们得到了数列的完整递推式
现在就容易知道的通项公式了,它是
又因为,的通项公式就出来了!
完了
//洛谷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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现