POJ 3734
阿新 • • 發佈:2018-11-11
題意
有 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;
}