ZOJ3690Choosing number

来源:互联网 发布:免费数据采集器 编辑:程序博客网 时间:2024/05/22 12:43
/*
题目:Choosing number
题目连接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3690
题目大意:


n个人站成一行,这儿有m个数字,1,2...m,每个人选择一个数字,但是如果相邻的两个人选择了同一个数字
这个数字不能超过k,请问他们有多少种选择数字的方式?


    解题思路:

我们一个人一个人的考虑,且从前往后,设两个函数G(i),F(i),G(i)表示前i-1个人选好后第i个人选择小于等于k数字时
的方案数,F(i)表示前i-1个人选好后第i个人选择大于k数字时的方案数,那我们得G(1)=k,F(1)=m-k;

 G(i)=F(i-1)*k+G(i-1)*(k-1); 
 当第i-1个人选大于k的数字时,第i个人可以选择1-k的任意数,所以加上F(i-1)*k,
 而当第i-1个人选小于等k的数字时,第i个人只要不和他选择的一样就可以了所以有(k-1)种情况;
 又因为选择1-k里面每个数字时等概率的,所以最后加上G(i-1)*(k-1);


 F(i)=(F(i-1)+G(i-1))*(m-k);
 当第i个人选大于k的数字时,第i-1个人可以任意选;


得到这个递推式后,我们可以构造一个转移矩阵得:


[F[i]]   [m-k,m-k  ]  [F[i-1]]
[G[i]]=[k,(k-1)/k] × [G[i-1]]


    然后用矩阵快速幂加速运算,最后的结果就是F(n)+G(n);

*/

#include<stdio.h>#include<string.h>#ifdef _WIN32#define _LL __int64#define _FIN "%I64d"#else #define _LL long long#define _FIN "%lld"#endif#define mod 1000000007_LL m,k;void init(_LL x[2][2]){x[0][0]=x[0][1]=m-k;x[1][0]=k;x[1][1]=(k-1);}void mult(_LL x[2][2],_LL y[2][2]){int i,j,k;_LL c[2][2];for(i=0;i<2;i++){for(j=0;j<2;j++){c[i][j]=0;for(k=0;k<2;k++)c[i][j]=(c[i][j]+(x[i][k]*y[k][j])%mod)%mod;}}for(i=0;i<2;i++) for(j=0;j<2;j++) x[i][j]=c[i][j];}_LL power(_LL y){_LL ans;_LL sum[2][2]={1,0,0,1};_LL x[2][2];init(x);while(y){if(y&1)mult(sum,x);mult(x,x);y>>=1;}ans=(sum[0][0]*(m-k)%mod+sum[0][1]*k%mod)%mod;ans=(ans+(sum[1][0]*(m-k))%mod+(sum[1][1]*k)%mod)%mod;return ans;}int main(){_LL n;while(scanf(_FIN,&n)!=EOF){scanf(_FIN,&m);scanf(_FIN,&k);if(n==1) printf(_FIN,m);else printf(_FIN,power(n-1));printf("\n");}return 0;}


原创粉丝点击