拼图(矩阵快速幂)



参数范围::1<=n<=10^15,1<=m<=7。

由m<7,n很大,可看出是用矩阵快速幂。复杂度O(log2(n)*2^(3m)) ~ O(10^8)可以接受。

但是,这题的建状态转移图与一般1*2的填充是不一样的,转移到另一个状态的填充方法可能不止一种

所以这题的状态转移图不再是01矩阵。

所以还是老样子,先建立状态转移图,然后矩阵快速幂。

矩阵快速幂最好写非递归,因为m=7的时候矩阵太大了。

再给出一些测试数据:

999999999999999    7  ---------->  847356131

99999999999999      6  ---------->  917572776

924   6 -------> 584569618

2        3  ------> 2

3        7  ------> 0

4        6  ------> 18

9        5  ------> 384

30     2  ------> 1024

1       1  ------> 0

9       2  ------> 8


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define M 1000000007
#define LL long long
using namespace std;
int K;
LL n,m;
struct Matrix{//矩阵定义
	LL a[128][128];
	Matrix(){for(int i=0;i<K;++i)for(int j=0;j<K;++j)a[i][j]=i==j;}
	Matrix(int x){memset(a,0,sizeof(a));}
	Matrix operator*(const Matrix &B)const{//矩阵乘法 
		Matrix C(0);
		for(int i=0;i<K;++i){
			for(int j=0;j<K;++j){
				for(int k=0;k<K;++k){
					C.a[i][j]=(C.a[i][j]+a[i][k]*B.a[k][j])%M;
				}
			} 
		}
		return C;
	}
	Matrix power(LL k){//非递归矩阵快速幂 
		Matrix A(*this),R;
		while(k){
			if(k&1) R=R*A;
			A=A*A;k>>=1;
		}
		return R;
	}
	void Show(){
		for(int i=0;i<K;++i){
			for(int j=0;j<K;++j){
				printf("%d ",a[i][j]);
			}
			printf("\n");
		}
	}
};
Matrix A(0);
int This;
void F(int now,int next){//now表示当前行状态,next表示下一行状态 
	if(now+1==K){//当前行填满,表示可以到达状态next。 
		A.a[This][next]++;//This 到 next 的路可能不止一条 
		return;
	}
	for(int k=0;k < m;++k){
		if((now>>k)&1) continue;
		int temp=1 << k;
		int now_,next_;
		//拼上四种图案,递归 
		now_=temp|(temp<<1);
		next_=temp;
		if(k+1<m&&!(now&now_||next&next_)) F(now|now_,next|next_);
		
		now_=temp|(temp<<1);
		next_=temp<<1;
		if(k+1<m&&!(now&now_||next&next_)) F(now|now_,next|next_);
		
		now_=temp;
		next_=temp|(temp<<1);
		if(k+1<m&&!(now&now_||next&next_)) F(now|now_,next|next_);
		
		now_=temp;
		next_=temp|(temp>>1);
		if(k&&!(now&now_||next&next_)) F(now|now_,next|next_);
		
		break;//这里不退出的话,会导致重复 
	}
}
void Init(){//建立状态转移矩阵 
	for(This=0;This < K ;++This){
		F(This,0);
	}
}
int main(){
	cin>>n>>m;K = 1;
	for(int i=0;i<m;++i) K <<= 1;
	Init();
	Matrix ANS=A.power(n);
	cout<<ANS.a[0][0] <<endl;
return 0;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值