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

问用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
#include
#include
#include
#include
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;ifor(int j=0;jfor(int k=0;kreturn 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;i1;
    for(int i=0;ifor(int j=0;jif(((~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;
}

你可能感兴趣的:(vijos,caioj,矩阵乘法)