经典问题——选择颜色

题目传送门

选择颜色

题目大意:

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

solution

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

  1. 第一种情况
    第n-1和第1个颜色相同,那么就有c-1种方案
  2. 第二种情况
    第n-1和第1个颜色不同,那么就有c-2种方案

然后我们考虑递推

设f[i]表示表示第i个和第1个相同的方案数,g[i]表示第i个和第1个不同的方案数。

那么则有公式

\[ \begin{cases} f[i] = g[i - 1] \times c \\ g[i] = f[i - 1] \times (c - 1) + g[i - 1] \times (c - 2)\\ \end{cases} \]
边界条件是:f[1]=c,g[1]=0。这个根据定义就可以看出来
然后就可以\(O(n)\)爆推。

可是这种方法还不足以过掉这道题。所以我们可以考虑矩阵快速幂。

\[ \begin{bmatrix} 0 & c \\ c-1 & c-2 \\ \end{bmatrix} \begin{bmatrix} f_i \\ g_i\\ \end{bmatrix} = \begin{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;
}

猜你喜欢

转载自www.cnblogs.com/ifmyt/p/9660287.html
今日推荐