hdu 5490 Simple Matrix 递推公式+逆元+组合数

本文介绍了一个结合等差数列和等比数列生成简单矩阵的问题,通过递推公式实现了矩阵中特定位置数值的有效计算,并提供了两种不同时间复杂度的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Simple Matrix

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 244    Accepted Submission(s): 85


Problem Description
As we know, sequence in the form of  an=a1+(n1)d  is called arithmetic progression and sequence in the form of  bn=b1qn1(q>1,b10)  is called geometric progression. Huazheng wants to use these two simple sequences to generate a simple matrix. Here is what he decides to do:
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=cn1,m+cn,m1 , where  n1  and  m1 .
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 .
 

Input
The first line of input contains a number  T  indicating the number of test cases ( T200 ).
For each test case, there is only one line containing six non-negative integers  b1,q,a1,d,n,m . ( 0n10000 ). All integers are less than  231 .
 

Output
For each test case, output a single line consisting of “Case #X: Y”.  X  is the test case number starting from 1.  Y  is  cn,m  module 1000000007.
 

Sample Input
  
2 3 10 1 1 3 3 5 2 1 10 4 2
 

Sample Output
  
Case #1: 423 Case #2: 140
 

Source

题目:第一行从第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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值