Q老師染磚(矩陣快速冪優化DP)

問題描述

衣食無憂的 Q老師 有一天突發奇想,想要去感受一下勞動人民的艱苦生活。

具體工作是這樣的,有 N 塊磚排成一排染色,每一塊磚需要塗上紅、藍、綠、黃這 4 種顏色中的其中 1 種。且當這 N 塊磚中紅色和綠色的塊數均爲偶數時,染色效果最佳。

爲了使工作效率更高,Q老師 想要知道一共有多少種方案可以使染色效果最佳,你能幫幫他嗎?

Input

第一行爲 T,代表數據組數。(1 ≤ T ≤ 100)

接下來 T 行每行包括一個數字 N,代表有 N 塊磚。(1 ≤ N ≤ 1e9)

Output

輸出滿足條件的方案數,答案模 10007。

Sample input

2
1
2

Sample output

2
6

解題思路

首先我們可以發現這個題有很明顯的子結構特性,因此可以使用DP來做,但是n的數據範圍十分大。我們考慮應該用什麼優化。

A[i]A[i]ii個格子,紅綠均爲偶數的方案數,但是這個狀態不夠,因爲當前狀態除了紅綠均爲偶數還有其他情況,我們還要添加兩個狀態。

B[i]B[i]ii個格子,紅綠均爲奇數的方案數,C[i]C[i]ii個格子,紅綠只有一個是偶數的方案數。

我們可以列出線性轉移方程:
{A[i]=2×A[i1]+C[i1]B[i]=2×B[i1]+C[i1]C[i]=2A[i1]+2×B[i1]+2×C[i1] \begin{cases} A[i] = 2 \times A[i-1] + C[i-1]\\ B[i] = 2 \times B[i-1] + C[i-1]\\ C[i]=2*A[i-1]+2\times B[i-1]+2\times C[i-1] \end{cases}
由於這是線性轉移方程,我們很容易想到矩陣快速冪優化,作出轉移矩陣如下:
ans[i]=[A[i]B[i]C[i]]=[201021222]ans[i1] ans[i]=\begin{bmatrix} A[i]\\ B[i]\\ C[i] \end{bmatrix} =\begin{bmatrix} 2&0&1\\ 0&2&1\\ 2&2&2\\ \end{bmatrix} *ans[i-1]
因此可以使用矩陣快速冪優化DP來做這道題,初始狀態A[1]=2,B[1]=0,C[1]=2A[1]=2, B[1]=0, C[1]=2

完整代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

struct Matrix{
    Matrix(int _size=0) {
        Size=_size; mat=new int*[Size];
        for (int i=0; i<Size; i++) mat[i]=new int[Size];
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                mat[i][j]=0;
    }
    Matrix(const Matrix& t){
        Size=t.Size; mat=new int*[Size];
        for (int i=0; i<Size; i++) mat[i]=new int[Size];
        memcpy(mat,t.mat,sizeof(mat));
    }
    ~Matrix(){
        for (int i=0; i<Size; i++) delete[] mat[i];
        delete[] mat;
    }
    Matrix operator*(const Matrix& t) const{
        Matrix ret(Size);
        for (int i=0; i<Size; ++i)
            for (int j=0; j<Size; ++j)
                for (int k=0; k<Size; ++k)
                    ret.mat[i][j]+=mat[i][k]*t.mat[k][j];
        return ret;
    }
    Matrix operator%(const int m) const {
        Matrix ret(Size);
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                ret.mat[i][j]=mat[i][j]%m;
        return ret;
    }
    Matrix& operator=(const Matrix& t){
        for (int i=0; i<Size; i++) delete[] mat[i];
        delete[] mat;
        Size=t.Size;
        mat=new int*[Size];
        for (int i=0; i<Size; i++) mat[i]=new int[Size];
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                mat[i][j]=t.mat[i][j];
        return *this;
    }
    void quick_pow(int x,int m){//x次冪模m
        Matrix ret(Size);
        for (int i=0; i<Size; i++) ret.mat[i][i]=1;
        while(x){
            if(x&1) {
                ret=ret*(*this); ret=ret%m;
            }
            *this=(*this)*(*this);
            *this=(*this)%m;
            x>>=1;
        }
        *this=ret;
    }
    void output(){
        printf("Size: %d\n",Size);
        for (int i=0; i<Size; i++){
            for (int j=0; j<Size; j++)
                printf("%d ",mat[i][j]);
            printf("\n");
        }
    }
    int Size;
    int** mat;
};
int getint(){
    int x=0,s=1; char ch=' ';
    while(ch<'0' || ch>'9'){ ch=getchar(); if(ch=='-') s=-1;}
    while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar();}
    return x*s;
}
int t,n;
int main(){
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    t=getint();
    while(t--){
        n=getint();
        Matrix mat1(3);
        mat1.mat[0][0]=2; mat1.mat[0][1]=0; mat1.mat[0][2]=1;
        mat1.mat[1][0]=0; mat1.mat[1][1]=2; mat1.mat[1][2]=1;
        mat1.mat[2][0]=2; mat1.mat[2][1]=2; mat1.mat[2][2]=2;
        mat1.quick_pow(n-1,10007);
        //mat1.output();
        long long ans=0;
        ans=mat1.mat[0][0]*2+mat1.mat[0][2]*2; ans%=10007;
        printf("%lld\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章