1. 程式人生 > >POJ 3734

POJ 3734

題意

有 n 個板子 四種顏色(紅綠藍黃),對 n 個板子塗色,在確保 紅綠色板子 均為偶數個的情況下 有多少種塗色方案(對10007取模)

n<1e9

 

首先 暴力搜尋就不用去想了,這道題要是做的話需要 dp 也就是遞推的方法 我們試想一下 對於當前 n 塊板子一共有幾種情況?

An   : 紅綠都達到偶數個了

Bn   : 只有紅或綠達到偶數個了

Cn   : 都沒有達到偶數個

既然DP的思想是由一種已知情況推測出未知情況, 那麼 對 An+1 Bn+1 Cn+1 我們都可以用已知的資訊推測出。

Ai+1 =  2 * Ai (接下來一塊不塗紅綠 塗藍或黃 兩種情況) + Bi (那種顏色不滿足 塗哪種顏色)

Bi+1 =  2 * Ai(隨意塗一個紅或綠) + 2 * Bi(隨意塗一個藍或黃) + 2 * Ci(塗紅或綠)

Ci+1 =  Bi(把湊夠偶數個那個顏色塗掉) + 2 * Ci(塗藍或黃)

這樣就湊出了三個遞推式

我們似乎發現了一些規律  回想一下之前見到的那個斐波那契數列題,這道題也可以利用矩陣的性質來快速完成掉

縱向還是 [Ai Bi Ci]  而我們需要一個 3*3矩陣, 那麼看一下遞推式 我們不難推出這個矩陣

          [    2    1    0    ]

X  =   [     2    2    2   ]         然後運用快速冪 就可以快速求解 得出的結果 我們就記錄在了 第一列 而 第一列第一行 是 Ai 

          [     0    1    2    ]

 

AC程式碼如下:(這次我嘗試了過載運算子)

 

    #include<iostream>
    #include<cstring>
    using namespace std;
    const int mod = 1e4+7;
    struct matrix
    {
        int m[3][3];
        matrix friend operator * (matrix a,matrix b)
	    {
		matrix tp;
		memset(tp.m,0,sizeof(tp.m));
		for(int i=0;i<3;++i)
		{
			for(int j=0;j<3;++j)
			{
				if(!a.m[i][j])
				{
					continue;
				}
				for(int k=0;k<3;++k)
				{
					tp.m[i][k]+=(a.m[i][j]*b.m[j][k])%mod;
					tp.m[i][k]%=mod;
				}
			}
		}
		return tp;
	    }
    };
    matrix base;
    int pow(matrix x,int n)
    {
        matrix ans={0};
	    for(int i=0;i<3;++i)
	    {
	 	    ans.m[i][i]=1;
	    }
        while(n)
        {
            if(n&1)
                ans = ans * x;
            x = x * x;
            n>>=1;
        }
        return ans.m[0][0];
    }
    int n;
    int main()
    {
        int test;
        cin>>test;
        while(test--)
        {
            cin>>n;
            matrix base={2,1,0,2,2,2,0,1,2};
            cout<<pow(base,n)<<endl;
        }
        return 0;
    }