一道记数题
笑死,一开始考虑只有一层的情况,推出递推公式之后成功跑偏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; }
我爱学习,学习使我快乐