FZU2289 项链(n种颜色组成长度为m的相邻不同色项链)

题目链接

http://acm.fzu.edu.cn/problem.php?pid=2289

题目

520要到了,Xenon打算送给Cherry一条项链,这条项链有m个镶孔,编号分别为0到m-1。Xenon手上有n种颜色不同的钻石,他想将钻石镶嵌在项链上,而且每个相邻的镶孔,镶嵌上的钻石颜色要不一样。

Xenon想知道他可以镶嵌出多少种不同的项链。

PS:第k号镶孔和第(k-1+m)%m以及(k+1+m)%m号镶孔这两个镶孔是相邻关系。假设m=9,那么0号和1号、8号相邻,3号和2号、4号相邻。

两条项链不同当且仅当存在编号k,两条项链的k号镶孔的钻石颜色不一样。

Input
题目包含多组测试数据,每组测试数据包含两个正整数n和m,分别表示钻石的颜色种数和一条项链所需要的钻石个数,以空格隔开。

n≤50,m≤10^18

Output
输出一个整数,表示Xenon可以制作出多少种项链。

由于答案很大,请将答案对1000000007(10^9+7)取余。

Sample Input
5 2
Sample Output
20

分析

当m=1,ans=n.
当m=2,ans=n*(n-1)
当m=3,ans=n*(n-1)*(n-2)
设d[m]表示由最多n种颜色组成的长度为m的相邻不同色项链。
从长为m-1进行推导:
加一个钻石,需要考虑所加钻石与第1个钻石是否一样,与第m-1个钻石是否一样。
如果第1个钻石与第m-1个钻石一样,那么第m-2个钻石因为相邻不同色原则一定会与第m-1个钻石不一样,即第m-2个钻石与第1个钻石不一样。故前m-2个钻石的组成的项链总数恰好满足d[m-2]。所加钻石颜色有n-1种选择,方案数为(n-1)*d[m-2]。
如果第1个钻石与第m-1个钻石不一样,那么前m-1个钻石组成的项链总数恰好为d[m-1],所加钻石颜色有n-2种选择,方案数为(n-2)*d[m-1]。
故d[m]=(n-2)*d[m-1]+(n-1)*d[m-2],m>=4时。
m=3时不满足上述递推公式,是因为当m=3时第1个钻石与第2个钻石(即第1个钻石与第m-1个钻石)一定不一样。
m最大为1e18.用矩阵快速幂求递推即可。

ps:64位输入输出不支持%lld,天秀。

AC代码

#include <cstdio>
#include <queue>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int maxn=1e5+100;
const ll mod=1e9+7;
struct matrix
{
    ll mat[2][2];
};
matrix mul_mod(matrix A,matrix B)
{
    matrix ans;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
    {
        ans.mat[i][j]=0;
        for(int k=0;k<2;k++)
        {
            ll tmp=A.mat[i][k]*B.mat[k][j]%mod;
            ans.mat[i][j]=(ans.mat[i][j]+tmp)%mod;
        }
    }
    return ans;
}
matrix pow_mod(matrix A,ll n)
{
    matrix ans=A,x=A;
    n--;
    while(n>0)
    {
        if(n&1) ans=mul_mod(ans,x);
        n>>=1;
        x=mul_mod(x,x);
    }
    return ans;
}
int main()
{
    ll n,m;
    while(~scanf("%I64d%I64d",&n,&m))
    {
        matrix A;
        if(m<=3)
        {
            if(m==1) printf("%I64d\n",n%mod);
            else if(m==2)printf("%I64d\n",n*(n-1)%mod);
            else if(m==3) printf("%I64d\n",n*(n-1)*(n-2));
            else printf("0\n");
            continue;
        }
        A.mat[0][0]=(n-2)%mod;
        A.mat[0][1]=1;
        A.mat[1][0]=(n-1)%mod;
        A.mat[1][1]=0;
        matrix ans;
        ans=pow_mod(A,m-3);
        matrix tmp;
        tmp.mat[0][0]=n*(n-1)*(n-2)%mod;
        tmp.mat[0][1]=n*(n-1)%mod;
        ans=mul_mod(tmp,ans);
        printf("%I64d\n",ans.mat[0][0]%mod);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值