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

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
2015 ACM/ICPC Asia Regional Hefei Online

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


你可能感兴趣的:(##ACM-ICPC编程题,##数学)