选择颜色
题目大意:
n个人排成一个环形,每个人要从c种颜色中选择一个。
牛牛希望相邻的人选择的颜色是不同的
问有多少种方案。
solution
首先我们考虑当插入第n个颜色时,它所处的情况
- 第一种情况
第n-1和第1个颜色相同,那么就有c-1种方案 - 第二种情况
第n-1和第1个颜色不同,那么就有c-2种方案
然后我们考虑递推:
设f[i]表示表示第i个和第1个相同的方案数,g[i]表示第i个和第1个不同的方案数。
那么则有公式
[egin{cases}
f[i] = g[i - 1] imes c
\
g[i] = f[i - 1] imes (c - 1) + g[i - 1] imes (c - 2)\
end{cases}
]
边界条件是:f[1]=c,g[1]=0。这个根据定义就可以看出来
然后就可以(O(n))爆推。
可是这种方法还不足以过掉这道题。所以我们可以考虑矩阵快速幂。
[egin{bmatrix} 0 & c \ c-1 & c-2 \ end{bmatrix}
egin{bmatrix} f_i \ g_i\ end{bmatrix}
=
egin{bmatrix} f_{i+1} \ g_{i+1}\ end{bmatrix}
]
矩阵公式就是如上。然后做n-1次快速幂就可以A掉这道题了
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cstdio>
using namespace std;
const int mod = 10007;
struct edge
{
int a[5][5];
};
int n,c;
edge muil(edge A,edge B){
edge C;
memset(C.a,0,sizeof(C.a));
for(int k=1;k<=2;k++)
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
C.a[i][j]+=((A.a[i][k]%mod)*(B.a[k][j]%mod))%mod,C.a[i][j]%=mod;
return C;
}
edge ksm(edge A,int y){
edge C;
memset(C.a,0,sizeof(C.a));
for(int i=1;i<=2;i++)C.a[i][i]=1;
while(y){
if(y&1)C=muil(C,A);
y>>=1;
A=muil(A,A);
}
return C;
}
edge A;
int D[3][3],E[3][3];
int main(){
scanf("%d%d",&n,&c);
memset(A.a,0,sizeof(A.a));
A.a[2][2]=c-2;
A.a[2][1]=c-1;
A.a[1][2]=1;
A=ksm(A,n-1);
printf("%d",A.a[2][1] * c % mod);
return 0;
}