笑死,一开始考虑只有一层的情况,推出递推公式之后成功跑偏wwww,然后死也找不出两层情况的递推公式
最后离考试结束还有30min的时候才猛然发现,淦可以直接按照每一列的情况暴力(其实也不暴力)转移啊。
设第一列的颜色是(1,2),再设0颜色是其他m-2种颜色的集合体。那么我们就有了(0,0)(0,1)(0,2)(1,0)(1,2)(2,0)(2,1)这七种状态。
然后我们只需要考虑相邻的两列之间这7种状态的笛卡尔积这7*7种转移的方案数,就可以构造转移矩阵A了
(0,0,0,0,1,0,0) * A^n这个向量的第5维的值就是所求(因为绕了一圈回来必须回到(1,2)这个状态)。
最后还需要再把这个乘上 m*(m-1) 枚举1和2具体是哪个颜色,就是答案了。
tip: 构造A其实才是这个题最难的部分,这里留给聪明的你们来思考
(然后我在考试结束前的一个if里少写了一个条件WA了,考试结束后10min才改对呜呜呜)
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int ha = 1e9+7;
inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
ll n;
int m;
struct matrix{
int a[9][9];
matrix(){
memset(a,0,sizeof(a));
}
matrix operator * (const matrix &u)const{
matrix r;memset(r.a,0,sizeof(r.a));
for(int k=0;k<9;k++)
for(int i=0;i<9;i++)
for(int j=0;j<9;j++) ADD(r.a[i][j],a[i][k] * (ll)u.a[k][j] % ha);
return r;
}
inline void construct(){
for(int i=0;i<9;i++)
for(int j=0;j<9;j++){
int c = i/3,d = i%3,A = j/3,B = j%3;
if((c>0 && A == c) || (d>0 && B==d) || (A == B && A>0)) continue;
if(A > 0 && B > 0) a[i][j] = 1;
else if((A>0) + (B>0) == 1){
if(A>0) a[i][j] = (d>0?max(m-2,0):max(m-3,0));
else a[i][j] = (c>0?max(m-2,0):max(m-3,0));
}
else{
if(c>0 && d>0) a[i][j] = max(m-2,0)*(ll)max(m-3,0)%ha;
else if(c>0 || d>0) a[i][j] = max(m-3,0)*(ll)max(m-3,0)%ha;
else a[i][j] = add(max(m-3,0),max(m-4,0) * (ll)max(0,m-4)%ha);
}
}
}
};
int main(){
cin>>n>>m;
if(m==1){
cout<<0<<endl;
return 0;
}
matrix x;
x.construct();
matrix ans;
for(int i=0;i<9;i++) ans.a[i][i] = 1;
for(;n;n>>=1,x=x*x) if(n&1) ans = ans * x;
cout<<ans.a[5][5] * (ll)m % ha *(ll)(m-1) %ha<<endl;
return 0;
}