P5074 Eat the Trees
思路
同样是插头DP,但是这题因为可以形成多个回路,所以左右括号是没有区别的,只需要01就可以表示了
注意if的嵌套关系
注意全零矩阵也要输出1
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int HASHsize = 400000;
int mat[20][20],n,m,T,pos[40];
int cnt[2],now,last,ans,endx,endy,fir[HASHsize+10],nxt[HASHsize+10],val[2][HASHsize+10],times[2][HASHsize+10];
void init(void){
memset(mat,0,sizeof(mat));
memset(cnt,0,sizeof(cnt));
now=0,last=0,ans=0,endx=0,endy=0;
memset(fir,0,sizeof(fir));
memset(nxt,0,sizeof(nxt));
memset(val,0,sizeof(val));
memset(times,0,sizeof(times));
}
void insert(int c,int num){
int t=c%HASHsize;
for(int i=fir[t];i;i=nxt[i]){
if(val[now][i]==c){
times[now][i]+=num;
return;
}
}
++cnt[now];
val[now][cnt[now]]=c;
times[now][cnt[now]]=num;
nxt[cnt[now]]=fir[t];
fir[t]=cnt[now];
}
void print(int x){
for(int i=0;i<m+1;i++)
printf("%lld",(x>>i)&1);
printf("\n");
}
void dp(void){
now=0;
insert(0,1);
for(int i=1;i<=n;i++){
for(int k=1;k<=cnt[now];k++)
val[now][k]<<=1;
for(int j=1;j<=m;j++){
last=now;
now^=1;
cnt[now]=0;
memset(fir,0,sizeof(fir));
memset(nxt,0,sizeof(nxt));
// printf("i=%lld j=%lld\n",i,j);
for(int k=1;k<=cnt[last];k++){
int state=val[last][k],num=times[last][k],plugL=(state>>(j-1))&1,plugU=(state>>(j))&1;
// printf("num=%lld\n",num);
// print(state);
// printf("plugL=%lld plugU=%lld\n",plugL,plugU);
if(mat[i][j]){
//新建连通分量
if((!plugL)&&(!plugU)){
if(mat[i+1][j]&&mat[i][j+1])
insert(state+(1<<(j-1))+(1<<(j)),num);
}
//合并联通分量
else if(plugL&&plugU){
if(i==endx&&j==endy)
ans+=num;
else
insert(state-(1<<(j-1))-(1<<(j)),num);
}
//延续联通分量
else{
if(plugL){
// printf("!\n");
if(mat[i+1][j])
insert(state,num);
if(mat[i][j+1])
insert(state-(1<<(j-1))+(1<<(j)),num);
}
if(plugU){
if(mat[i+1][j])
insert(state-(1<<(j))+(1<<(j-1)),num);
if(mat[i][j+1])
insert(state,num);
}
}
}
else{
if((!plugL)&&(!plugU))
insert(state,num);
}
}
}
}
}
signed main(){
scanf("%lld",&T);
pos[0]=1;
for(int i=1;i<30;i++)
pos[i]=pos[i-1]<<1;
while(T--){
bool isok=true;
init();
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%lld",&mat[i][j]);
if(mat[i][j]){
endx=i;endy=j;
isok=false;
}
}
// printf("endx=%lld endy=%lld\n",endx,endy);
if(!isok){
dp();
printf("%lld\n",ans);
}
else{
printf("1\n");
}
}
return 0;
}