试题 算法提高 瓷砖铺放 增强
资源限制
时间限制: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;
}