【poj3420】递推式转矩阵乘法

【poj3420】递推式转矩阵乘法

历史性的时刻!!!

推了一晚上!和hyc一起萌萌哒地推出来了!!

被摧残蹂躏的智商啊!!!

然而炒鸡高兴!!

(请不要介意蒟蒻的内心独白。。)

 

设a[i]为扫到第i行时的方案数。

易知,对于一行1*4的格子,只有一种方案把它铺满。

首先,对于当前的第i行,如果它不和第i-1行有联系(也就是它是独立的一行),那么就有1*a[i-1]=a[i-1]种方案。

如果第i行和第i-1行有联系(2行间互相联系),那么共有一下四种方案:

 

 

如果第i行、第i-1行、第i-2行都有联系(3行间两两联系),那么共有两种方案(此图以及的轴对称图形):

 

 

如果第i行、第i-1行、第i-2行、第i-3行都有联系(4行间两两联系),那么共有三种方案(上面两种方案加下面一种):

 

……

 

一直递推下去,我们可以发现:

2行间相互联系 --> 4种方案

3行间相互联系 --> 2种方案

4行间相互联系 --> 3种方案

……

奇数行相互联系(n>2) --> 2种方案

偶数行相互联系(n>2) --> 3种方案

 

所以,我们可以得出:

a[i]=a[i-1]+4*a[i-2]+2*(a[i-3]+a[i-5]+……+a[(i&1)?0:1])+3*(a[i-4]+a[i-6]+……+a[(i&1)?1:0]);

 

设s=a[i-3]+a[i-5]+……+a[(i&1)?0:1],t=a[i-4]+a[i-6]+……+a[(i&1)?1:0];

该递推式可以转化为矩阵乘法:

 

 

代码如下:

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

const int N=1000000010;
int Mod;
struct node{
    int s[5][5];
    int sx,sy;
};

node mult(node a,node b)
{
    node t;
    t.sx=a.sx;t.sy=b.sy;
    memset(t.s,0,sizeof(t.s));
    for(int i=1;i<=a.sx;i++)
        for(int j=1;j<=b.sy;j++)
            for(int k=1;k<=a.sx;k++)
            {
                t.s[i][j]+=(a.s[i][k]*b.s[k][j])%Mod;
                t.s[i][j]%=Mod;
            }
    return t;
}

void quickpow(int n)
{
    if(n==0) {printf("0\n");return ;}
    node a;
    a.s[1][1]=1%Mod,a.s[1][2]=4%Mod,a.s[1][3]=2%Mod,a.s[1][4]=3%Mod;
    a.s[2][1]=1%Mod,a.s[2][2]=0%Mod,a.s[2][3]=0%Mod,a.s[2][4]=0%Mod;
    a.s[3][1]=0%Mod,a.s[3][2]=1%Mod,a.s[3][3]=0%Mod,a.s[3][4]=1%Mod;
    a.s[4][1]=0%Mod,a.s[4][2]=0%Mod,a.s[4][3]=1%Mod,a.s[4][4]=0%Mod;
    a.sx=4;a.sy=4;
    node b;
    b.s[1][1]=b.s[2][2]=b.s[3][3]=b.s[4][4]=1%Mod;
    b.s[1][2]=b.s[1][3]=b.s[1][4]=0%Mod;
    b.s[2][1]=b.s[2][3]=b.s[2][4]=0%Mod;
    b.s[3][1]=b.s[3][2]=b.s[3][4]=0%Mod;
    b.s[4][1]=b.s[4][2]=b.s[4][3]=0%Mod;
    b.sx=4;b.sy=4;
    while(n)
    {
        if(n&1) b = mult(a,b);
        a = mult(a,a);
        n>>=1;
    }
    node c;
    c.sx=4;c.sy=1;
    c.s[1][1]=1%Mod;c.s[2][1]=c.s[3][1]=c.s[4][1]=0%Mod;
    c = mult(b,c);
    printf("%d\n",c.s[1][1]);
    return ;
}

int main()
{
    while(1)
    {
        int x;
        scanf("%d%d",&x,&Mod);
        if(x==0) return 0;
        quickpow(x);
    }
    return 0;
}
View Code

 

posted @ 2015-09-28 22:15 拦路雨偏似雪花 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值