18730 涂色问题 (快速幂的两种写法)

m^{n}

解题思路:组合计算的逆向思维法,发疯方案数F=总方案数S-不发疯方案数N。

不发疯方案数计算:第一间牛舍有m选择,第二间有m-1选择,同样第三间有m-1选择N=m*(m-1)^{n} , 总方案数S=m^{n},因此 F=S-N=m^{n}-m*(m-1)^{n}需要注意的是题目中的n很大,求一个数的n次幂,当n很大时用循环求取会超时,需采用快速幂算法。

快速幂是一种典型的分治算法。当我们要求m^{n}时,可以先求出x=m^{\frac{n}{2}},那么m^{n} = x*x同样的道理求取x时,可以先求m^{\frac{n}{4}},再做平方。特别注意,当n为奇数时,m^{n} = m*x*x

递归快速幂模板:

long long ksm(ll n,ll m)/**< 求n的m次幂,注意快速幂算法尽量用longlong类型 */
{
    if(m==1)
        return n%mod;
    long long temp;
    temp=ksm(n,m/2);/**< temp是n的m/2次幂 */
    temp=temp*temp%mod;
    if(m%2==1) /**< 如果m是奇数,那要多乘1个n */
        temp=n*temp%mod;
    return temp;
}

 另一种写法:可以注意到上面的递归算法是单递归算法,这类递归一般也能用循环方式来解决。

此处用到了二进制位的知识。举例说明:n的11次幂,11的二进制1011
n的11次幂可以看成是n^{8}*n^{2}*n^{1} ,因为二进制第三位是0,所以没有n^{4}。用循环依次获得n^{1},n^{2},n^{4},n^{8}.......,选择需要的进行乘法,比如11我们会选择n^{1},n^{2},n^{8}

long long ksm(ll n,ll m)
{
    ll ans=1;
    while(m) /**< 可以用十进制11,即二进制 1011来模拟和思考 */
    {
        if(m&1)/**< 二进制最后一位是1,奇数 */
            ans=ans*n%mod;
        n=n*n%mod; /**< n成为n^2,再循环会变成n^4,n^8...... */
        m=m>>1;/**< m右移一位 */
    }
    return ans;
}

 本题目完整代码:

#include <iostream>
typedef long long ll;
using namespace std;
const ll mod=1000000007 ;
long long ksm(ll n,ll m)/**< 求n的m次幂,注意快速幂算法尽量用longlong类型 */
{
    if(m==1)
        return n%mod;
    long long temp;
    temp=ksm(n,m/2);/**< temp是n的m/2次幂 */
    temp=temp*temp%mod;
    if(m%2==1) /**< 如果m是奇数,那要多乘1个n */
        temp=n*temp%mod;
    return temp;
}
long long ksm2(ll n,ll m)
{
    ll ans=1;
    while(m) /**< 可以用十进制11,即二进制 1011来模拟和思考 */
    {
        if(m&1)/**< 二进制最后一位是1,奇数 */
            ans=ans*n%mod;
        n=n*n%mod; /**< n成为n^2,再循环会变成n^4,n^8...... */
        m=m>>1;/**< m右移一位 */
    }
    return ans;
}
int main()
{
    ll m,n;
    cin>>n>>m;
    cout<<(ksm(m,n)-m*ksm(m-1,n-1)%mod+mod)%mod;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值