【JZOJ4787】数格子【矩阵乘法】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/4787
求用 1 × 2 1\times 2 1×2的骨牌铺满 4 × n 4\times n 4×n的方格的方案数。


思路:

这种类型的题目很显然是公式或者规律的。况且 n ≤ 1 0 9 n\leq 10^9 n109这种 O ( n ) O(n) O(n)都不可以过的还能有什么做法XD
惯例:打表找规律。

n n n12345678910
a n s ans ans151136952817812245633618061

肉眼看了 5 m i n 5min 5min还是什么都没看出来。
这是个好网站
往下翻,就可以看到
a ( n ) = a ( n − 1 ) + 5 ∗ a ( n − 2 ) + a ( n − 3 ) − a ( n − 4 ) a(n)=a(n-1)+5*a(n-2)+a(n-3)-a(n-4) a(n)=a(n1)+5a(n2)+a(n3)a(n4)

所以利用这个公式就可以拿到 60 p t s 60pts 60pts的高分了。
这个显然是可以用矩阵乘法搞的呀。
在这里插入图片描述

这样复杂度就是 log ⁡ \log log级别的了。


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;

const ll A[5][5]=
{
	{0,0,0,0,0},
	{0,1,5,1,-1},
	{0,1,0,0,0},
	{0,0,1,0,0},
	{0,0,0,1,0}
};

int n,MOD;
ll f[5],a[5][5];

void mul(ll f[5],ll a[5][5])
{
	ll c[5]={0,0,0,0,0};
	for (int i=1;i<=4;i++)
		for (int j=1;j<=4;j++)
			c[i]=(c[i]+a[i][j]*f[j])%MOD;
	memcpy(f,c,sizeof(c));
}

void mulself(ll a[5][5])
{
	ll c[5][5];
	memset(c,0,sizeof(c));
	for (int i=1;i<=4;i++)
		for (int j=1;j<=4;j++)
			for (int k=1;k<=4;k++)
				c[i][j]=(c[i][j]+a[i][k]*a[k][j])%MOD;
	memcpy(a,c,sizeof(c));
}

/*
1,5,11,36,95,281,781,2245,6336,18061
a(n)=a(n-1)+5*a(n-2)+a(n-3)-a(n-4)
*/

int main()
{
	while (scanf("%d%d",&n,&MOD)==2 && n && MOD)
	{
		if (n==1) {printf("1\n"); continue;}
		if (n==2) {printf("5\n"); continue;}
		if (n==3) {printf("11\n"); continue;}
		if (n==4) {printf("36\n"); continue;}
		memcpy(a,A,sizeof(A));
		f[1]=36,f[2]=11,f[3]=5,f[4]=1;
		n-=4;
		while (n)
		{
 			if (n&1) mul(f,a);
			mulself(a);
			n>>=1;
		}
		cout<<(f[1]%MOD+MOD)%MOD<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值