题目链接:ZOJ3690
题意:有n个人,每个人可以从m个数中选取其中的一个数,而且如果两个相邻的数相同,则这个数不能超过k,问这样的数一共有多少种选择,结果对1e9+7取模。
分析:考虑以下2个函数
f[n]是第n个人时的答案数
num[n]是第n个人时小于等于k的答案数
不难得出
f[n]=f[n-1]*m-num[n-1]
num[n]=f[n-1]*k-num[n-1]
我们构造如下矩阵:
然后套个矩阵快速幂的板子就好
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
const int maxn=100;
const ll Mod=1e9+7;
ll tmp[maxn][maxn];
void multi(ll a[][maxn],ll b[][maxn],int n)//n是矩阵大小
{
int i,j,k;
memset(tmp,0,sizeof tmp);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
for(k=1;k<=n;k++)
{
tmp[i][j]+=a[i][k]*b[k][j];
tmp[i][j]%=Mod;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
a[i][j]=tmp[i][j];
}
ll res[maxn][maxn];
void Pow(ll a[][maxn],ll m,int n)// a 是初始矩阵 res是答案数组 m是幂,n是矩阵大小
{
for(int i=1;i<maxn;i++)
for(int j=1;j<maxn;j++)
res[i][j]=(i==j);
while(m)
{
if(m&1) multi(res,a,n); //res=res*a;
multi(a,a,n); //a=a*a
m>>=1;
}
}
ll a[maxn][maxn];
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k))
{
a[1][1]=m;a[2][2]=-1;
a[1][2]=-1;a[2][1]=k;
if(n==0)
{
printf("0\n");
return 0;
}
Pow(a,n-1,2);
ll ans=0;
ans=(ans+m*res[1][1])%Mod;
ans=(ans+k*res[1][2])%Mod;
ans=(ans+Mod)%Mod;
printf("%lld\n",ans);
}
return 0;
}
/*
题目的意思是有n个人和m个数和一个k,现在每个人可以选择一个数,
但是要求如果相邻的两个人选择相同的数,那么这个数要大于k
*/