矩阵快速幂处理一类线性递推组问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38786088/article/details/82721657

最近两场比赛,都涉及到此类问题!以前没注意过这种问题,都在状态转移方程推出后,止步于n太大!事后总结,线性递推可以用矩阵来表示,进而快速幂解决n太大的问题!

例1:牛客小白月赛7 J 方格填色

题意:n×m矩阵填黑白两色,行相同相邻两个不能同为白色,相邻两列不全为黑色
(n≤5,m≤1e18),问填色的方案数mod 1e9+7的值.

令黑色为1,白色为0,将一列的颜色01由下往上排布,即An-1An-2……A0
(Ai为1表示该列第i+1格子为黑色,0即为白色)
也就是2进制表示,00……0~11……1,每列有2^n种可能出现的值。(状态)
(每一列的长度最长为5,即状态数最多为32,2的5次方)

设置状态
状态表示

限制条件:

相邻两列不全为黑色—>第i列为111……1,第i+1列不能为状态111……1
相邻两列的同行格不同为白色—>第i列的j位为0,第i+1列的j位不为0

相邻状态关系结论:

前面状态为x,相邻状态为y。
x、y满足 x|y = 11……1(2进制),(x=y=11……1除外)

状态转移方程:

f[x][m] 定义:第m列涂色状态为x,前面1~m列染色满足限制条件的方案数

f [ x ] [ m ] = ( 0 y 2 n 1 , x y = 2 n 1 , x = y 2 n 1 ) f [ y ] [ m 1 ] , ( 0 x 2 n 1 ) f[x][m] = \sum_{(0\leq y \leq 2^n-1,x|y=2^n-1,x=y\neq 2^n-1)}f[y][m-1],(0\leq x \leq 2^n-1)
举个例子: n=2时,每列有4种状态,枚举第m列的状态

1. 0 f [ 0 ] [ m ] = f [ 3 ] [ m 1 ] 1. 状态为0,f[0][m] = f[3][m-1] ( m 0 m 1 ) (第m列全为白色,不能同数位为0,第m-1列全为黑色)
2. 1 f [ 1 ] [ m ] = f [ 2 ] [ m 1 ] + f [ 3 ] [ m 1 ] 2. 状态为1,f[1][m] = f[2][m-1]+f[3][m-1]
( m 2 m 1 2 1 ) (第m列第2格为白色,第m-1列第2格必须为黑色,第1格可黑可白)
3. 2 f [ 2 ] [ m ] = f [ 1 ] [ m 1 ] + f [ 3 ] [ m 1 ] 3. 状态为2,f[2][m] = f[1][m-1]+f[3][m-1]
( m 1 m 1 1 2 ) (第m列第1格为白色,第m-1列第1格必须为黑色,第2格可黑可白)
4. 3 f [ 3 ] [ m ] = f [ 0 ] [ m 1 ] + f [ 1 ] [ m 1 ] + f [ 2 ] [ m 1 ] 4. 状态为3,f[3][m] = f[0][m-1]+f[1][m-1]+f[2][m-1]
( m m 1 ) (第m列全为黑色,即只要第m-1列不全为黑色即可)
整理如下:
{ f [ 0 ] [ m ] = f [ 3 ] [ m 1 ] f [ 1 ] [ m ] = f [ 2 ] [ m 1 ] + f [ 3 ] [ m 1 ] f [ 2 ] [ m ] = f [ 1 ] [ m 1 ] + f [ 3 ] [ m 1 ] f [ 3 ] [ m ] = f [ 0 ] [ m 1 ] + f [ 1 ] [ m 1 ] + f [ 2 ] [ m 1 ] \begin{cases} f[0][m] &= f[3][m-1]\\ f[1][m] &= f[2][m-1]+f[3][m-1]\\ f[2][m] &= f[1][m-1]+f[3][m-1]\\ f[3][m] &= f[0][m-1]+f[1][m-1]+f[2][m-1] \end{cases}
当m不大时,可以去递推,但m超大时,必须另外想方法,因为是线性递推,而且递推状态不多,可以矩阵表示,考虑矩阵快速幂

[ f [ 0 ] [ m ] f [ 1 ] [ m ] f [ 2 ] [ m ] f [ 3 ] [ m ] ] = [ 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 ] [ f [ 0 ] [ m 1 ] f [ 1 ] [ m 1 ] f [ 2 ] [ m 1 ] f [ 3 ] [ m 1 ] ] \left[ \begin{matrix} f[0][m] \\ \\ f[1][m]\\ \\ f[2][m]\\ \\ f[3][m]\\ \end{matrix} \right] = \left[ \begin{matrix} 0&0&0&1\\ \\ 0&0&1&1\\ \\ 0&1&0&1\\ \\ 1&1&1&0\\ \end{matrix}\right] * \left[ \begin{matrix} f[0][m-1]\\ \\ f[1][m-1]\\ \\ f[2][m-1]\\ \\ f[3][m-1]\\ \end{matrix} \right]
递推得到:
[ f [ 0 ] [ m ] f [ 1 ] [ m ] f [ 2 ] [ m ] f [ 3 ] [ m ] ] = [ 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 ] m 1 [ f [ 0 ] [ 1 ] f [ 1 ] [ 1 ] f [ 2 ] [ 1 ] f [ 3 ] [ 1 ] ] \left[ \begin{matrix} f[0][m] \\ \\ f[1][m]\\ \\ f[2][m]\\ \\ f[3][m]\\ \end{matrix} \right] = \left[ \begin{matrix} 0&0&0&1\\ \\ 0&0&1&1\\ \\ 0&1&0&1\\ \\ 1&1&1&0\\ \end{matrix}\right] ^{m-1}* \left[ \begin{matrix} f[0][1]\\ \\ f[1][1]\\ \\ f[2][1]\\ \\ f[3][1]\\ \end{matrix} \right]
[ f [ 0 ] [ 1 ] f [ 1 ] [ 1 ] f [ 2 ] [ 1 ] f [ 3 ] [ 1 ] ] = [ 1 1 1 1 ] \left[ \begin{matrix} f[0][1]\\ \\ f[1][1]\\ \\ f[2][1]\\ \\ f[3][1]\\ \end{matrix} \right] = \left[ \begin{matrix} 1\\ \\ 1\\ \\ 1\\ \\ 1\\ \end{matrix} \right]

所以你只需要获取矩阵系数即可!

#include <bits/stdc++.h>
#define llt long long
using namespace std;

const int N=40;
const llt mod = 1e9+7;
llt a[N][N],ans[N][N],tmp[N][N];

void multi(llt x[][N],llt y[][N],int len){
    for(int i=0;i<len;++i)
      for(int j=0;j<len;++j){
        tmp[i][j] = 0;
        for(int k=0;k<len;++k)
           tmp[i][j] += x[i][k]*y[k][j]%mod;
           tmp[i][j] %= mod;
      }
    for(int i=0;i<len;++i)
      for(int j=0;j<len;++j)
        x[i][j] = tmp[i][j];

}

void quick_mod(llt x,int len){
    for(;x;x>>=1,multi(a,a,len))
        if(x&1) multi(ans,a,len);
}
int main(){

    int n;
    llt m;
    scanf("%d%lld",&n,&m);
    int status = 1<<n;
    for(int i=0;i<status;++i)
      for(int j=0;j<status;++j)
          if(i==status-1&&j==status-1)
               a[i][j] = 0;
          else if((i|j)==status-1)
               a[i][j] = 1;
          else a[i][j] = 0;
    for(int i=0;i<status;++i)
      for(int j=0;j<status;++j)
          if(i==j) ans[i][j] = 1;
          else ans[i][j] = 0;
    quick_mod(m-1,status);
    llt Ans = 0;
    for(int i=0;i<status;++i)
      for(int j=0;j<status;++j)
         Ans = (Ans+ans[i][j])%mod;
    printf("%lld\n",Ans);
    return 0;
}

例2:18焦作网络赛 L、Poor God Water

题意: 由字符C、W、F组成的字符串中,任意长度为3的子串不为"CCC"、“MMM”、“FFF”、“MCF”、“FCM”、“CMC”、“CFC”; 问有多少种不同的字符串符合要求,答案mod 1 000 000 007.

这里写图片描述

即字符串前部分已经构建,影响后面m个字符的枚举的只有其最后两个字符,所以设计dp数组f的含义:前面两个为‘#*’,后面长度为n的满足要求的不同字符串的个数;状态转移即枚举接下来的字符C、M、F,删去非法的,上面图片也就是状态转移过程!将状态编号,得到以下状态转移方程
{ f [ 0 ] [ m ] = f [ 1 ] [ m 1 ] + f [ 2 ] [ m 1 ] f [ 1 ] [ m ] = f [ 4 ] [ m 1 ] + f [ 5 ] [ m 1 ] f [ 2 ] [ m ] = f [ 7 ] [ m 1 ] + f [ 8 ] [ m 1 ] f [ 3 ] [ m ] = f [ 0 ] [ m 1 ] + f [ 1 ] [ m 1 ] f [ 4 ] [ m ] = f [ 3 ] [ m 1 ] + f [ 5 ] [ m 1 ] f [ 5 ] [ m ] = f [ 6 ] [ m 1 ] + f [ 7 ] [ m 1 ] + f [ 8 ] [ m 1 ] f [ 6 ] [ m ] = f [ 0 ] [ m 1 ] + f [ 2 ] [ m 1 ] f [ 7 ] [ m ] = f [ 3 ] [ m 1 ] + f [ 4 ] [ m 1 ] + f [ 5 ] [ m 1 ] f [ 8 ] [ m ] = f [ 6 ] [ m 1 ] + f [ 7 ] [ m 1 ] \begin{cases} f[0][m] &amp;= f[1][m-1]+f[2][m-1]\\ f[1][m] &amp;= f[4][m-1]+f[5][m-1]\\ f[2][m] &amp;= f[7][m-1]+f[8][m-1]\\ f[3][m] &amp;= f[0][m-1]+f[1][m-1]\\ f[4][m] &amp;= f[3][m-1]+f[5][m-1]\\ f[5][m] &amp;= f[6][m-1]+f[7][m-1]+f[8][m-1]\\ f[6][m] &amp;= f[0][m-1]+f[2][m-1]\\ f[7][m] &amp;= f[3][m-1]+f[4][m-1]+f[5][m-1]\\ f[8][m] &amp;= f[6][m-1]+f[7][m-1]\\ \end{cases}
矩阵表达式:
[ f [ 0 ] [ m ] f [ 1 ] [ m ] f [ 2 ] [ m ] f [ 3 ] [ m ] f [ 4 ] [ m ] f [ 5 ] [ m ] f [ 6 ] [ m ] f [ 7 ] [ m ] f [ 8 ] [ m ] ] = [ 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 ] [ f [ 0 ] [ m 1 ] f [ 1 ] [ m 1 ] f [ 2 ] [ m 1 ] f [ 3 ] [ m 1 ] f [ 4 ] [ m 1 ] f [ 5 ] [ m 1 ] f [ 6 ] [ m 1 ] f [ 7 ] [ m 1 ] f [ 8 ] [ m 1 ] ] \left[ \begin{matrix} f[0][m] \\ \\ f[1][m]\\ \\ f[2][m]\\ \\ f[3][m]\\ \\ f[4][m]\\ \\ f[5][m]\\ \\ f[6][m]\\ \\ f[7][m]\\ \\ f[8][m]\\ \end{matrix} \right] = \left[ \begin{matrix} 0&amp;1&amp;1&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;1&amp;1&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;1&amp;1\\ \\ 1&amp;1&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;1&amp;0&amp;1&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;1&amp;1&amp;1\\ \\ 1&amp;0&amp;1&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;1&amp;1&amp;1&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;1&amp;1&amp;0\\ \end{matrix}\right] * \left[ \begin{matrix} f[0][m-1] \\ \\ f[1][m-1]\\ \\ f[2][m-1]\\ \\ f[3][m-1]\\ \\ f[4][m-1]\\ \\ f[5][m-1]\\ \\ f[6][m-1]\\ \\ f[7][m-1]\\ \\ f[8][m-1]\\ \end{matrix} \right]

递推得到:
[ f [ 0 ] [ m ] f [ 1 ] [ m ] f [ 2 ] [ m ] f [ 3 ] [ m ] f [ 4 ] [ m ] f [ 5 ] [ m ] f [ 6 ] [ m ] f [ 7 ] [ m ] f [ 8 ] [ m ] ] = [ 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 ] m [ f [ 0 ] [ 0 ] f [ 1 ] [ 0 ] f [ 2 ] [ 0 ] f [ 3 ] [ 0 ] f [ 4 ] [ 0 ] f [ 5 ] [ 0 ] f [ 6 ] [ 0 ] f [ 7 ] [ 0 ] f [ 8 ] [ 0 ] ] \left[ \begin{matrix} f[0][m] \\ \\ f[1][m]\\ \\ f[2][m]\\ \\ f[3][m]\\ \\ f[4][m]\\ \\ f[5][m]\\ \\ f[6][m]\\ \\ f[7][m]\\ \\ f[8][m]\\ \end{matrix} \right] = \left[ \begin{matrix} 0&amp;1&amp;1&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;1&amp;1&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;1&amp;1\\ \\ 1&amp;1&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;1&amp;0&amp;1&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;1&amp;1&amp;1\\ \\ 1&amp;0&amp;1&amp;0&amp;0&amp;0&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;1&amp;1&amp;1&amp;0&amp;0&amp;0\\ \\ 0&amp;0&amp;0&amp;0&amp;0&amp;0&amp;1&amp;1&amp;0\\ \end{matrix}\right]^m * \left[ \begin{matrix} f[0][0] \\ \\ f[1][0]\\ \\ f[2][0]\\ \\ f[3][0]\\ \\ f[4][0]\\ \\ f[5][0]\\ \\ f[6][0]\\ \\ f[7][0]\\ \\ f[8][0]\\ \end{matrix} \right]
[ f [ 0 ] [ 0 ] f [ 1 ] [ 0 ] f [ 2 ] [ 0 ] f [ 3 ] [ 0 ] f [ 4 ] [ 0 ] f [ 5 ] [ 0 ] f [ 6 ] [ 0 ] f [ 7 ] [ 0 ] f [ 8 ] [ 0 ] ] = [ 1 1 1 1 1 1 1 1 1 ] \left[ \begin{matrix} f[0][0] \\ \\ f[1][0]\\ \\ f[2][0]\\ \\ f[3][0]\\ \\ f[4][0]\\ \\ f[5][0]\\ \\ f[6][0]\\ \\ f[7][0]\\ \\ f[8][0]\\ \end{matrix} \right] = \left[ \begin{matrix} 1 \\ \\ 1\\ \\ 1\\ \\ 1\\ \\ 1\\ \\ 1\\ \\ 1\\ \\ 1\\ \\ 1\\ \end{matrix} \right]

即快速矩阵幂得到系数矩阵的m次方后,即矩阵的所有元素之和即为答案!

/*


0 CC --> CM,CF 1,2
1 CM --> MM,MF 4,5
2 CF --> FM,FF 7,8
3 MC --> CC,CM 0,1
4 MM --> MC,MF 3,5
5 MF --> FC,FM,FF 6,7,8
6 FC --> CC,CF    0,2
7 FM --> MC,MM,MF 3,4,5
8 FF --> FC,FM    6,7


*/
#include <bits/stdc++.h>
#define llt long long
#define mp make_pair
#define fi first
#define se second
using namespace std;

const llt mod = 1e9+7;
const int N = 20;
const int maxn = 9;
llt tmp[N][N],ans[N][N],a[N][N];
llt A[N][N]={
    {0,1,1,0,0,0,0,0,0},
    {0,0,0,0,1,1,0,0,0},
    {0,0,0,0,0,0,0,1,1},
    {1,1,0,0,0,0,0,0,0},
    {0,0,0,1,0,1,0,0,0},
    {0,0,0,0,0,0,1,1,1},
    {1,0,1,0,0,0,0,0,0},
    {0,0,0,1,1,1,0,0,0},
    {0,0,0,0,0,0,1,1,0},
};
void multi(llt a[][N],llt b[][N],int n){
    for(int i=0;i<n;++i)
      for(int j=0;j<n;++j){
         tmp[i][j] = 0;
         for(int k=0;k<n;++k)
            (tmp[i][j]+=a[i][k]*b[k][j]%mod)%=mod;
      }

    for(int i=0;i<n;++i)
      for(int j=0;j<n;++j)
        a[i][j] = tmp[i][j];
}

void quick_mod(llt m,int n){
    for(int i=0;i<n;++i)
      for(int j=0;j<n;++j)
         ans[i][j] = (i==j),a[i][j]=A[i][j];

    for(;m;m>>=1,multi(a,a,n))
        if(m&1) multi(ans,a,n);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        llt n;
        scanf("%lld",&n);
        if(n==1) printf("3\n");
        else{
            quick_mod(n-2,maxn);
//            for(int i=0;i<maxn;++i)
//              for(int j=0;j<maxn;++j)
//                 printf("%lld%c",ans[i][j]," \n"[j==maxn-1]);
            llt res = 0;
            for(int i=0;i<maxn;++i)
              for(int j=0;j<maxn;++j)
                 (res+=ans[i][j])%=mod;
            printf("%lld\n",res);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38786088/article/details/82721657