您的位置:首页 > 大数据 > 人工智能

[caioj 1487及vijos 1194,利用矩阵乘法解决的经典题目九]有趣的domino

2017-08-26 16:55 429 查看
问用1*2的多米诺骨牌填满m*n的矩阵有多少种方案,结果需要mod p。

这道题看上去跟矩阵乘法八竿子都打不着边,但是我们可以转换模型。其实我们可以将这个m*n矩阵的状态通过位运算转换成二进制状态,具体就是先假设i是前一个状态,而j是这一个状态,那么i状态到j状态的条件就是i or j=k 且 i and j=s[x] (0<=x<8),s[]={0,3,6,12,15,24,27,30};为什么这样做,因为当i状态和j状态在某个位置必须至少有一个是1,如果i是1,j是0,那么就说明这个地方不用放骨牌,如果i是0,j是1那么就是放骨牌。如果i和j都是1,那么说明原来这个地方有骨牌了,现在还有,那么一定是横着放的骨牌,而s数据的作用就是判断i与j是不是连续的1,转换成二进制就可以看出来了。最后只要条件符合,将这些二进制状态转换成一个矩阵,再平方n次,这题就解决了。(注意:vijos与caioj的n,m输入顺序是相反的)

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
struct node
{
long long a[35][35];
node()
{
memset(a,0,sizeof(a));
}
};
int he;
long long p;
int s[8]={0,3,6,12,15,24,27,30};
node chengfa(node a,node b)
{
node c;
for(int i=0;i<he;i++)
{
for(int j=0;j<he;j++)
{
for(int k=0;k<he;k++)
{
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%p;
}
}
}
return c;
}
int main()
{
int m;long long n;
node pre,ans;
he=1;
scanf("%lld%d%lld",&n,&m,&p);
for(int i=1;i<=m;i++)he*=2;
for(int i=0;i<he;i++)ans.a[i][i]=1;
for(int i=0;i<he;i++)
{
for(int j=0;j<he;j++)
{
if(((~i)&j)==((~i)&(he-1)))
{
int bk=0;
for(int k=0;k<8;k++)
{
if((i&j)==s[k])bk=bk||(i&j)==s[k];
}
pre.a[i][j]=bk;
}
}
}
long long x=n;
while(x>0)
{
if(x%2==1)ans=chengfa(pre,ans);
pre=chengfa(pre,pre);
x/=2;
}
printf("%lld\n",ans.a[he-1][he-1]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息