经典问题——选择颜色

题目传送门

选择颜色

题目大意:

n个人排成一个环形,每个人要从c种颜色中选择一个。
牛牛希望相邻的人选择的颜色是不同的
问有多少种方案。

solution

首先我们考虑当插入第n个颜色时,它所处的情况

  1. 第一种情况
    第n-1和第1个颜色相同,那么就有c-1种方案
  2. 第二种情况
    第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;
}
原文地址:https://www.cnblogs.com/ifmyt/p/9660287.html