矩阵快速幂 zoj-3690 Choosing number

本文介绍了一道来自ZJU在线评测系统的编程题,该题要求计算一组人员选择数字时满足特定条件的方案总数。通过使用动态规划方法,并结合矩阵快速幂优化递推过程,实现了高效求解。

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

 

题目链接:

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4973

 

题目意思:

有n个人,有1——m个数,每个人可以选择1个数,要求相邻的两个人如果选的数相同则必须大于k,求选数的种数。

 

解题思路:

dp[n][1]表示第n个人选大于k的数的总的种数,dp[n][2]表示第n个人选<=k的数的总的种数。

则  dp[n][1]=(m-k)*dp[n-1][1]+(m-k)*dp[n-1][2]

      dp[n][2]=k*dp[n-1][1]+(k-1)*dp[n-1][2]

构造矩阵

m-k  m-k    dp[n-1][1]    dp[n][1]

 k      k-1      dp[n-1][2]       dp[n][2]

 

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

#define ll long long
#define mm  1000000007
/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/

//矩阵快速幂,快速幂

ll Mod(ll m,ll n)  //快速幂求m^n%mm
{
    ll res=1;

    while(n)
    {
        if(n&1)
            res=(res*m)%mm;
        m=(m*m)%mm;
        n=n>>1;
    }
    return res;
}


ll save[3][3];


int main()
{
    ll n,m,k;

    while(scanf("%lld%lld%lld",&n,&m,&k)!=EOF)
    {
       if(k==0)
        {
            printf("%lld\n",Mod(m,n));
            continue;
        }
        if(n==1)
        {
            printf("%lld\n",m);
            continue;
        }

        save[1][1]=m-k,save[1][2]=m-k;
        save[2][1]=k,save[2][2]=k-1;

        ll ans1=m-k,ans2=k;
        n--;

        while(n)
        {
            if(n&1)
            {
               ll temp1=((save[1][1]*ans1)%mm+(save[1][2]*ans2)%mm)%mm;
               ll temp2=((save[2][1]*ans1)%mm+(save[2][2]*ans2)%mm)%mm;

               ans1=temp1;
               ans2=temp2;
            }
            n=n>>1;   //以后写矩阵相乘的话直接用三个循环来写,这样写的话容易出错
            ll a1=((save[1][1]*save[1][1])%mm+(save[1][2]*save[2][1])%mm)%mm;
            ll a2=((save[1][1]*save[1][2])%mm+(save[1][2]*save[2][2])%mm)%mm;
            ll b1=((save[2][1]*save[1][1])%mm+(save[2][2]*save[2][1])%mm)%mm;
            ll b2=((save[2][1]*save[1][2])%mm+(save[2][2]*save[2][2])%mm)%mm;

            save[1][1]=a1,save[1][2]=a2;
            save[2][1]=b1,save[2][2]=b2;

        }

        printf("%lld\n",(ans1+ans2)%mm);


    }
    return 0;
}







 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值