试题 算法提高 瓷砖铺放 增强

试题 算法提高 瓷砖铺放 增强

资源限制
时间限制:3.0s 内存限制:256.0MB
问题描述
  给了一nxm的方格,用1x3的瓷砖铺满,请问有多少种铺法。瓷砖可以旋转。
输入格式
  输入的第一行包含两个整数,n, m。
输出格式
  输出一个整数,表示总的铺法数。这个数可能很大,请输出对65521取余的结果。
样例输入
3 3
样例输出
2
数据规模和约定
  对于100%的数据,1<=n<=10^15,1<=m<=9。

在这里插入代码片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 65521;
const ll M=1<<9;
ll a[M*M+8];//用于并查集
ll b[M*M+8];//用于记录编号 
ll c[1600];//用于记录节点 
ll tem[400][400];//邻接矩阵 
ll ans[400][400];
ll f[400][400];
ll m,n;
ll len;
bool rr[M*M+8];//入度判断 
bool cc[M*M+8];//出度判断 
vector<vector<ll> >num((1<<18)+7);
int find(int i){
	if(a[i]==i)return a[i];
	else return a[i]=find(a[i]);
}
void sset(ll i,ll j){
	ll x=find(i);
	ll y=find(j);
	if(x!=y)a[x]=y;
}
void dfs(int i,int start1,int start2,int nex1,int nex2){
	if(i==m){
		num[(start1)*(1<<m)+start2].push_back((nex1)*(1<<m)+nex2);
		cc[(start1)*(1<<m)+start2]=1;
		rr[(nex1)*(1<<m)+nex2]=1;
		sset((start1)*(1<<m)+start2,(nex1)*(1<<m)+nex2);
	}else{
		//横放 
		if( (start1&(1<<i))==0 && ( (start2&(1<<i)) )==0 ){
				dfs(i+1,start1,start2,(nex1|(1<<i)),(nex2|(1<<i)));
		}
		//纵放 
		if(i+2<m && (start1&(1<<i))==0 && (start1&(1<<(i+1)))==0 && (start1&(1<<(i+2)))==0 ){
			dfs(i+3,start1,start2,nex1,nex2);
		}
		//不放 
		if((start1&(1<<i))>0){
			dfs(i+1,start1,start2,nex1,nex2);
		}
	}
}
void cf(int h){
	memset(f,0,sizeof(f));
	if(h==1){//进行tem的自乘
		for(int k=0;k<len;k++){
			for(int i=0;i<len;i++){
				ll zz=tem[i][k];
				if(zz==0)continue;
				for(int j=0;j<len;j++){
					f[i][j]=(f[i][j]+zz*tem[k][j])%mod;
				}
			}
		}
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++)tem[i][j]=f[i][j];
		}
	}else{//进行ans*tem 
		for(int k=0;k<len;k++){
			for(int i=0;i<len;i++){
				ll zz=ans[i][k];
				if(zz==0)continue;
				for(int j=0;j<len;j++){
					f[i][j]=(f[i][j]+zz*tem[k][j])%mod;
				}
			}
		}
		for(int i=0;i<len;i++){
			for(int j=0;j<len;j++)ans[i][j]=f[i][j];
		}
	}
}
void ch(){
	ll t=1;
	for(int j=0;j<len;j++)
	for(int i=0;i<len;i++)ans[j][i]=tem[j][i];
	n--;
	while(n){
	//	cout<<n<<endl;
		if(n&1)cf(0);
		cf(1);
		n=n>>t;
	}
}
int main(){
	//m列n行
	int fff=1<<20;
	scanf("%lld%lld",&n,&m);
	//并查集初始化 
	for(int i=0;i<(1<<(m*2))+3;i++)a[i]=i;
	for(int i=0;i<(1<<m);i++){
		for(int j=0;j<(1<<m);j++)
		dfs(0,i,j,j,0);
	}
	len=0;
	if(rr[0]==0||cc[0]==0){
		cout<<0<<endl;
		return 0;
	}
	for(int i=0;i<(1<<(m*2))+7;i++){
		//与零号目标节点连通,并且拥有入度和出度 
		if(find(0)==find(i)&&rr[i]&&cc[i]){ 
			 b[i]=len;c[len]=i;len++;
		}
	}
	//cout<<len<<endl;
	//建立关系矩阵 
	for(int i=0;i<len;i++){
		int x=b[c[i]];//第一个节点编号;
		for(int j=0;j<num[c[i]].size();j++){ 
			if(rr[num[c[i]][j]]&&cc[num[c[i]][j]]){
				int y=b[num[c[i]][j]];
				tem[x][y]=1;
			}
		}
	}
	//n阶可达矩阵计算 
	ch();
	cout<<ans[0][0]<<endl;
	return 0;
}
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值