zoj 3690

题目链接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;
}

猜你喜欢

转载自blog.csdn.net/qq_41626975/article/details/82216083
ZOJ