模拟赛 T2
乍一看题似乎是poj2411简化版 (poj2411)。
然而一看数据范围就懵了,10^9,只能用矩阵快速幂优化了。
为方便,把这个图设为N行4列。
二进制数位上的1表示这一列的这一块是一个竖着的1*2骨牌的上面一半。
状态 i 能转移到 j,当且仅当 i & j ==0 (这样1一定对应0,表示补全竖着的1*2骨牌),且 i | j 的二进制表示中每一段连续的0都有偶数个(表示横着放的骨牌,不能是奇数个)。(借鉴了李煜东学长的书☺)
若状态 i 能转移到 j ,则把邻接矩阵的a[i][j]赋值为1 。
前几天讲图论时提到邻接矩阵 a^k 中的每一个元素表示 i 到 j 恰走 k 条路的方案数。
这里同理,就是从最开始什么都没有放到最后都放完(也相当于什么都没有)恰放N行的方案数。
所以这样就完了~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
ll n,m,ans;
struct Matrix{
ll a[20][20];
void init()
{
memset(a,0,sizeof a);
for(int i=0;i<16;i++) a[i][i]=1;
}
}lj;
Matrix mul(Matrix x,Matrix y)
{
Matrix z;
memset(z.a,0,sizeof z.a);
for(int i=0;i<16;i++)
for(int j=0;j<16;j++)
for(int k=0;k<16;k++)
z.a[i][j]=(z.a[i][j]+(x.a[i][k]*y.a[k][j])%m)%m;
return z;
}
Matrix ksm(Matrix x,ll y)
{
Matrix ans;
ans.init();
while(y)
{
if(y&1) ans=mul(ans,x);
x=mul(x,x);
y>>=1;
}
return ans;
}
bool judge(int x)
{
if(x==0||x==3||x==9||x==12||x==15) return 1;
return 0;
}
void pre()
{
for(int i=0;i<16;i++)
for(int j=0;j<16;j++)
if(!(i&j)&&judge(i|j)) lj.a[i][j]=1;
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
pre();
while(1)
{
scanf("%lld%lld",&n,&m);
if(n==0&&m==0) break;
Matrix x=ksm(lj,n);
ans=x.a[0][0];
printf("%lld\n",ans);
}
return 0;
}