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

本文介绍了一种利用位运算和矩阵快速幂解决多米诺骨牌填充矩阵问题的方法,通过转换模型将问题转化为状态转移矩阵的形式,并提供了一个具体的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值