#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7,maxs=(1<<17)+5;//17^2<300
ll dp[maxs<<1],*pre,*cur;//dp开两倍maxs好让dp分成前后两部分。让pre,cur来回指
int main(){
int t,S,n,m;
cin>>t;
while(t--){
scanf("%d%d",&n,&m);
if(m>17) m^=n,n^=m,m^=n;//通过异或交换
//始终让m为小的,不超过maxs的
if((n&1)&&(m&1)){
printf("0\n");continue;
}
S=(1<<m)-1;
pre=dp,cur=dp+maxs;//滚动数组。+maxs就是指后面一半
memset(dp,0,sizeof(dp));
pre[0]=1;//
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
//每一个s用完立即清除pre(所以是s++),而不是留着mems
for(int s=0;s<=S;pre[s++]=0){
if(pre[s]==0) continue;
//上一次,这个未出现就不考虑。类似背包的上一趟这个位置为-1吧
if(s&(1<<j)){//向上铺
cur[s&~(1<<j)]=(cur[s&~(1<<j)]+pre[s])%mod;
continue;//如果往上了,可能不可能不铺!!
}
cur[s|(1<<j)]=(cur[s|(1<<j)]+pre[s])%mod;//不铺
if(s&(1<<j-1)&&j>0)//向左铺 。下面有j-1,所以j>0
cur[s&~(3<<j-1)]=(cur[s&~(3<<j-1)]+pre[s])%mod;
//3=1+2 往左铺有两格所以权为1和权为2的都要移。左即j-1
}
swap(pre,cur); //变的是指针。滚动数组的精髓吧
}
printf("%d\n",pre[0]);
}
}
zjhu1030铺砖块(状压dp)
最新推荐文章于 2024-10-01 18:52:56 发布