地址:http://acm.bit.edu.cn/mod/programming/view.php?id=670
用1*1*2的積木裝滿2*2*n的矩陣,一共有幾種方法。答案mod p輸出(1 <= n <= 1,000,000,000, 1 < p <= 1,000,000)
這題想了我好幾天,終於想明白每一層只有三種可能:不放向下的(情況0),放兩個相鄰的向下的(情況1),放四個向下的(情況2)。則由狀態壓縮DP的思想可以得到遞推公式:
dp[i][0]=dp[i-1][0]*2+dp[i-1][1]+dp[i-1][2];
dp[i][1]=dp[i-1][0]*4+dp[i-1][1];
dp[i][2]=dp[i-1][0];
初始條件
dp[0][0]=1,dp[0][1]=dp[0][2]=0;
這是由類似1022的思想想出來的。。然而n數據規模巨大,o(n)的遞推上去都不能被接受。。一開始想到的是因爲mod p,是否可以跑出循環節,結果仍舊超時,說明循環節都有可能很長。。
正解應該是矩陣快速冪優化遞推關係!
構造矩陣[2 4 1] 答案即變爲該矩陣的n次方的(0,0)位置的值!然後用矩陣快速冪瞬解。
[1 1 0]
[1 0 0]
好題。。
補充:劉果大神推出的另一個遞推公式:f(n)=3f(n-1)+3f(n-2)-f(n-3)
#include<iostream>
using namespace std;
typedef struct MATRIX
{
__int64 a[3][3];
}matrix;
int n,p,i,j;
matrix operator * (matrix x,matrix y)
{
matrix ans;
for(i=0;i<=2;i++)
{
for(j=0;j<=2;j++)
{
ans.a[i][j]=(x.a[i][0]*y.a[0][j]%p+x.a[i][1]*y.a[1][j]%p+x.a[i][2]*y.a[2][j]%p)%p;
}
}
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&p))
{
matrix I={2,4,1,1,1,0,1,0,0};
matrix t={1,0,0,0,1,0,1,0,0};
while(n>1)
{
if(n%2) t=t*I;
n/=2,I=I*I;
}
t=t*I;
printf("%d\n",t.a[0][0]);
}
return 0;
}