传球问题-矩阵快速幂

Description:

传球问题:

一共m个人,n次传球。

从小明手中传出,经过n次回到小明A手里的方案数。

Answer:

首先,我们知道,不管是A还是B,都不能自己传给自己,也就是说不能出现AA, BB这种情况。

设经过n次传球,回到小明手中的方案数为 a n a_n an​。(第一种传球方式)

设经过n次传球,不回到小明手中的方案数为 b n b_n bn​​。(第二种传球方式)

很容易知道 b 1 = 1 , b 2 = m − 2 b_1=1,b_2=m-2 b1=1,b2=m2

第一球传球方式: ​​
在这里插入图片描述

第二种传球方式: 两种情况,一个是经过A传到B, 一个是经过不是A也不是B的传给B。

在这里插入图片描述

根据上述的式子我们可以得出:
b n = b n − 2 ∗ ( m − 1 ) + b n − 1 ∗ ( m − 2 ) b_n=b_{n-2}*(m-1)+b_{n-1}*(m-2) bn=bn2(m1)+bn1(m2)
a n a_n an可以根据第一个式子 a n = b n − 1 ∗ ( m − 1 ) a_n=b_{n-1}*(m-1) an=bn1(m1)计算得到。

这样我们求 a n a_n an可以转到求 b n − 1 b_{n-1} bn1​上面。所有的递推式基本都可以转换到矩阵快速幂上面来。之前介绍斐波那契的时候提到过,大家可以找一下我的博客,看一下。接下来,我们就可以得到下面的式子:

在这里插入图片描述
上面的 a 1 , a 2 , a 3 , a 4 a_1, a_2, a_3, a_4 a1,a2,a3,a4没有其他的意义,只是代表 A n − 2 A^{n-2} An2的四个项。

所以, b n − 1 = a 1 + a 2 ∗ ( m − 2 ) b_{n-1}=a_1+a_2*(m-2) bn1=a1+a2(m2)。带入第一个式子, a n a_n an就求出来了。

下面举个例子,具体的题目:

小明现在正在带球组织进攻,目前他附近有n位队友可以进行传球配合。

小明作为主力射手,他希望和队友通过k次传球后,球还在自己脚下,并完成射门。

请计算这其中有多少种不同的传球方式。

输入:

正整数n和m,分别表示小明身边的队友数和传球次数。

输出:

不同的传球方式数(由于该数字可能很大,请输出总方式数对 10^9+7 的余数即可)

数据规模

对 20% 的数据:n = 1, 1 <= m <= 20

对 40% 的数据:1<= n <= 2, 1 <= m <= 10^5

对 70% 的数据:1 <= n <= 10, 1 <= m <= 10^18

对 100% 的数据: 1 <= n <= 10^9, 1 <= m <= 10^18

样例1输入

1 3

样例1输出

0

样例2输入

2 4

样例2输出

6

解题过程:
在这里插入图片描述

AC代码:

#include<bits/stdc++.h>
#include<iostream>

#define MAXNUM 1000000007
typedef unsigned long long ll;

using namespace std;
ll n, m;
//n个人, m次传球
//2*2的矩阵乘法
vector<vector<ll>> multiply(vector<vector<ll>> a, vector<vector<ll>> b){
    vector<vector<ll>> res {{0,0}, {0,0}};
    for(ll i=0; i<a.size(); i++){
        for(ll j=0; j<b.size(); j++){
            res[i][j] = (((a[i][0]%MAXNUM)*(b[0][j]%MAXNUM))%MAXNUM+((a[i][1]%MAXNUM)*(b[1][j]%MAXNUM))%MAXNUM)%MAXNUM;
        }
    }
    return res;
}
//矩阵快速幂
vector<vector<ll>> fib_matrix(vector<vector<ll>> a, ll n){
    //矩阵的0次幂是单位矩阵E
    if(n == 0) return {{1,0},{0,1}};
    else if( n&1 ){
        return  multiply(fib_matrix(a, n-1), a);
    }else{
        vector<vector<ll>> temp = fib_matrix(a, n/2);
        return multiply(temp, temp);
    }
}
int main(){
    cin>>n>>m;
    n = n+1;
    //A[m](res)表示 从小明出发 传m次 传到小明的方案数
    //B[m]表示从小明出发,传m次,传到B手中(不是小明)的方案数
    //A[m]=B[m-1]*(n-1)
    //B[m]=B[m-2]*(n-1)+B[m-1]*(n-2)
    if(m==1) return 0;
    //if(m==2) return n-1;
    vector<vector<ll>> res = fib_matrix({{0,1},{n-1,n-2}}, m-2);
    cout<<(((res[0][0]%MAXNUM+((res[0][1]%MAXNUM)*((n-2)%MAXNUM))%MAXNUM)%MAXNUM)*((n-1)%MAXNUM))%MAXNUM<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值