题目链接https://vjudge.net/problem/ZOJ-3690
题目如下
There are n people standing in a row. And There are m numbers, 1.2...m. Every one should choose a number. But if two persons standing adjacent to each other choose the same number, the number shouldn't equal or less than k. Apart from this rule, there are no more limiting conditions.
And you need to calculate how many ways they can choose the numbers obeying the rule.
Input
There are multiple test cases. Each case contain a line, containing three integer n (2 ≤ n ≤ 108), m (2 ≤ m ≤ 30000), k(0 ≤ k ≤ m).
Output
One line for each case. The number of ways module 1000000007.
Sample Input
4 4 1
Sample Output
216
这道题关键在于推导
设F(n)表示前n个人第n个人选择的数大于k的个数,G(n)表示的是前n个人第n个人选择的数小于等于k的个数
那么F(n) = F(n-1)*(m-k)+G(n-1)*(m-k) , G(n) = F(n-1)*k+G(n-1)*(k-1) , 那么最后的结果就是F(n)+G(n);
那么我们可以构造出矩阵
| m-k m-k | | F(n-1) | | F(n) |
| k k-1| * | G(n-1) | => | G(n) |
F(1) = m-k , G(1) = k
然后用矩阵快速幂
代码如下
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int n,m,k;
struct matrix
{
ll s[2][2];
}base;
matrix operator*(matrix a,matrix b)
{
matrix c;
memset(c.s,0,sizeof(c.s));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
c.s[i][j]+=(a.s[i][k]%mod)*(b.s[k][j]%mod)%mod;
return c;
}
ll pow_mod()
{
if(n==1)return m;
matrix a;
memset(a.s,0,sizeof(a.s));
memset(base.s,0,sizeof(base.s));
for(int i=0;i<2;i++)a.s[i][i]=1;
base.s[0][0]=base.s[0][1]=(m-k)%mod;
base.s[1][0]=k%mod,base.s[1][1]=(k-1)%mod;
int y=n-1;
while(y)
{
if(y&1)a=a*base;
base=base*base;
y>>=1;
}
ll sum=0;
sum+=(a.s[0][0]*(m-k)%mod+a.s[0][1]*k%mod);
sum%=mod;
sum+=(a.s[1][0]*(m-k)%mod+a.s[1][1]*k%mod);
return sum%mod;
}
int main()
{
while(cin>>n>>m>>k)
{
ll ret;
ret=pow_mod();
cout<<ret<<endl;
}
return 0;
}