题目:第一行从第1个数开始是等比数列,第一列从第一个数开始是等差数列(0,0)没有用
对于A(n,m) = A(n-1,m)+A(n,m-1)
打表以后容易发现是一个杨辉三角,如果把(n,m)看成是杨辉三角的顶部元素,那么每个位置上的C(i,j)就表示A(n,m)=A(i,j)*C(i,j)+.....
所以第一行,第一列的系数就可以求了。对于第i列上的元素:
系数为C(n+m-i-1,m-1),这个系数可以用组合数递推公式算,先算C(m-1,m-1)
然后(给出递推公式,由C(x,y) = C(x-1,y)+C(x-1,y-1)导出)
C(x+1,m-1) = C(x,m-1)+C(x,m-2)
= C(x,x-m+1) + C(x,x-m+2)
=C(x,x-m+1) + { C(x,x-m+1) * (m-1) / (x-m+2)}
看行的等比:(行和列可以分开考虑,不影响的):
b, b *q, b*q^2, ...... (第一行)记为
b1,b2,b3,......,bn
第二行的数关于bi的系数可以得到为
b1, b1+b2, b1+b2+b3, ..... , b1+b2+....bn
考虑第二行第i个位置的数字为
{b*q^i-1}/(q-1) = b*q^i/(q-1) - b/(q-1)= b*q/(q-1) * q(i-1) - b/(q-1)
考虑B = b*q/(q-1) C = -b/(q-1)
那么可以看做C是第二行上第一列位置的数的贡献,那么 a2 += C即可
而B又构成新的等比数列,可以用于下一行递推,公比为q
所以可以O(n)的递归出结果。可以做到以下两个复杂度的实现
O(n)的做法
#include
#include
#include
#include
#define ll long long
#define maxn 10007
using namespace std;
#define mod 1000000007
int inv[maxn];
//求逆元
int pow(int a,int n){
int ans = 1;
while(n){
if(n&1) ans= (ll)ans*a%mod;
a =(ll)a*a%mod;
n /= 2;
}
return ans;
}
//初始化,线性求逆元
void init(){
inv[1] = inv[0] = 1;
for(int i = 2;i < maxn; i++)
inv[i] = -(ll)mod/i*inv[mod%i]%mod+mod;
}
int com[maxn];
int main(){
init();
int t,tt=1,b,q,a,d,n,m;
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d%d%d",&b,&q,&a,&d,&n,&m);
a %= mod, q %= mod, d %= mod, b %= mod;
int invq = pow(q-1,mod-2);
com[n] = 1;
for(int i = n-1;i >= 1; i--){
com[i] = (com[i+1]+(ll)com[i+1]*(m-1)%mod*inv[n-i])%mod;
}
int ans = 0;
for(int i = 1;i <= n; i++){
//等比项
b = (ll)b*invq%mod;
ans = (ans + (ll)com[i] * (a - b) ) % mod;
//等差项
a += d;
if(a >= mod) a -= mod;
if(ans < 0) ans += mod;
b = (ll)b*q%mod;
}
ans = (ans + (ll)b*pow(q,m-1))%mod;
ans = (ans + mod)%mod;
printf("Case #%d: %d\n",tt++,ans);
}
return 0;
}
O(nlog(mod)的做法
#include
#include
#include
#include
#define ll long long
#define maxn 10007
using namespace std;
#define mod 1000000007
int inv[maxn];
//求逆元
int pow(int a,int n){
int ans = 1;
while(n){
if(n&1) ans= (ll)ans*a%mod;
a =(ll)a*a%mod;
n /= 2;
}
return ans;
}
//初始化,线性求逆元
void init(){
inv[1] = inv[0] = 1;
for(int i = 2;i < maxn; i++)
inv[i] = -(ll)mod/i*inv[mod%i]%mod+mod;
}
//求组合数
int C(ll n,int m){
int ans = 1;
for(int i = 0;i < m; i++)
ans = (ll)ans*(n-i)%mod*inv[i+1]%mod;
return ans;
}
int main(){
init();
int t,tt=1,b,q,a,d,n,m;
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d%d%d",&b,&q,&a,&d,&n,&m);
a %= mod, q %= mod, d %= mod, b %= mod;
int com = C((ll)n+m-2,n-1),invq = pow(q-1,mod-2);
int ans = 0;
for(int i = 1;i <= n; i++){
// cout<= mod) a -= mod;
//等比项
b = (ll)b*invq%mod;
ans = (ans - (ll)b*com)%mod;
if(ans < 0) ans += mod;
b = (ll)b*q%mod;
//更新组合数
com = (ll)com*(n-i)%mod*pow(m+n-i-1,mod-2)%mod;
}
ans = (ans + (ll)b*pow(q,m-1))%mod;
ans = (ans + mod)%mod;
printf("Case #%d: %d\n",tt++,ans);
}
return 0;
}