24.The Little Architect
Description
Silence is a little architect, he likes to make all kinds of buildings with his building blocks. Now he has a new problem. He want to build a building of size 2 * 2 * n. But he only has one kind of block of size 1 * 1 * 2. Now he wants to know how many ways he can build the building.
Input
There are several lines in the input. Each line is a set of input data. There are two integers n and p (1 <= n <= 1,000,000,000, 1 < p <= 1,000,000) in each test case. The input is end with EOF.
Output
For each test case, output a single line contains one integer which is the number of differentways he can build the building. Because of the answer may be very large, you just need to output the answer mod p.
Sample in
1 999997 2 999997 6 999997
Sample out
2 9 1681
思路分析与讲解:
大家应该已经知道这题的做法了
矩阵快速幂+状态方程
显然,矩阵快速幂不是难点,这个不会的稍微查一下资料就能知道了,那么我们的难点就在于如何推导出状态方程
一开始我是毫无思路的瞎推倒,但是了解了状态压缩DP这个东西之后,我很顺利的推出了公式,而且是巨简单的公式,包学包会!
我们已经知道,入手是从第N层的状态开始。
于是可以得到六种情况
0 0 0 0 1 1 1 0 0 1 1 1
0 0 1 1 0 0 1 0 0 1 1 1
第一情况,也就是说注定了倒数第二层也是竖放的,于是这种情况的总数为f(n-2)
第六种情况,我们可以知道,第一层是满放的,于是总数为2*f(n-1)
中间四种情况可以一起考虑,于是直接给它一个状态,我们设为G,于是
我们现在就有公式
f(n) = 2*f(n-1)+f(n-2)+4*G(n-1)---------------1式
对于这个公式,我们发现现在我们只要找到G状态的递推方程即可
于是,研究一下G状态,会知道,倒数第二层的状态为
0 0 0 1
0 0 0 1
两种情况
对于第一种情况,直接用回之前的经验,有f(n-2)种
对于第二种情况,也是之前的经验,有G(n-2)种
于是乎,我们又得到了一个很重要的状态方程,G(n-1)=f(n-2)+G(n-2)---------------------2式
联立1,2两式,我们可以轻易的得到最后的方程
f(n) = 3*f(n-1) + 3*f(n-2) - f(n-3)
至此,此题也就迎刃而解~
最后再贴一下本人的AC代码吧,仅供参考:
#include <stdio.h>
#include <string.h>
typedef struct
{
long long num[3][3] ;
}node;
long long p ;
node fun(node a , node b )
{
int i , j , k ;
node c ;
memset(c.num,0,sizeof(c.num)) ;
for( i = 0 ; i < 3 ; i++ ) {
for( j = 0 ; j < 3 ; j++ ) {
for( k = 0 ; k < 3 ; k++ )
c.num[i][j]+=a.num[i][k]*b.num[k][j] ;
c.num[i][j]%=p ;
}
}
return c ;
}
int main()
{
long long n , b[3] = { 2 , 9 , 32 } ;
node a , t ;
while( ~scanf("%lld%lld",&n,&p) ) {
memset(a.num,0,sizeof(a.num)) ;
a.num[0][0] = 3 ;
a.num[1][0] = 3 ;
a.num[2][0] = -1 ;
a.num[0][1] = 1 ;
a.num[1][2] = 1 ;
memset(t.num,0,sizeof(t.num)) ;
t.num[0][0] = 1 ;
t.num[1][1] = 1 ;
t.num[2][2] = 1 ;
if(n>3) {
n = n-3 ;
while(n>=1) {
if(n%2==1)
t = fun(t,a);
n = n/2 ;
a = fun(a,a);
}
long long temp = (b[2]*t.num[0][0]+b[1]*t.num[1][0]+b[0]*t.num[2][0])%p ;
if( temp < 0 ) temp+=p ;
printf("%lld\n",temp) ;
}
else printf("%lld\n",b[n-1]%p) ;
}
return 0;
}