Simple Matrix
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 244 Accepted Submission(s): 85
Use the geometric progression as the first row of the simple matrix: c0,n=bn
Use the arithmetic progression as the first column of the simple matrix: cn,0=an
Calculate the item at n -th row, m -th column of the simple matrix as cn,m=cn−1,m+cn,m−1 , where n≥1 and m≥1 .
Given the two sequences, Huazheng wants to know the value of cn,m , but he is too busy with his research to figure it out. Please help him to work it out. By the way, you can assume that c0,0=0 .
For each test case, there is only one line containing six non-negative integers b1,q,a1,d,n,m . ( 0≤n≤10000 ). All integers are less than 231 .
2 3 10 1 1 3 3 5 2 1 10 4 2
Case #1: 423 Case #2: 140
题目:第一行从第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<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#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<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#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<<ans<<" "<<com<<" "<<endl;
//等差项
ans = (ans + (ll)com * a) % mod;
a += d;
if(a >= 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;
}