![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int cube=(int)1e9; 6 const int mod=2601; 7 int n,m; 8 struct Data_Analysis//有关高精 9 { 10 int bit[6];//存储答案的高精数组,bit[i]中不止一个数,会存在一个很大的数 11 inline void Clear() {memset(bit,0,sizeof(bit));} 12 Data_Analysis() {Clear();}//初始化清空 13 inline void Set(int t) 14 { 15 Clear(); 16 while(t) {bit[++bit[0]]=t%cube; t/=cube;}//类似于高精中的对10取模及做乘法,只不过变为对1e9取模做乘法 17 } 18 inline int &operator [](int x) {return bit[x];} 19 inline void Print() 20 { 21 printf("%d",bit[bit[0]]);//bit[0]可能小于0,但是此时也需要输出0,所以需要这么一步 22 for(int i=bit[0]-1;i>0;i--) printf("%d",bit[i]);//正常倒着输出 23 printf("\n"); 24 } 25 inline Data_Analysis operator + (Data_Analysis b)//高精加 26 { 27 Data_Analysis c; c.Clear(); c[0]=max(bit[0],b[0])+1;//两数相加的位数最多为位数大的那个+1 28 for(int i=1;i<=c[0];i++) {c[i]+=bit[i]+b[i]; c[i+1]+=c[i]/cube; c[i]%=cube;} 29 while(!c[c[0]]) c[0]--;//排除最高位的0 30 return c; 31 } 32 inline void operator += (Data_Analysis b) {*this=*this+b;}//*this"返回当前对象的引用 或者说返回该对象本身 还是当前对象的克隆" 33 inline void operator = (int x) {Set(x);}//最初bit[0]=0,计数用,所以在最初赋值时也直接用set,把bit[0]留出来 34 }Ans; 35 struct Hash_Sheet//有关hash 36 { 37 Data_Analysis val[mod];//方案数 38 int key[mod],size,hash[mod];//所表示的真实状态,元素个数,是hash表中第几个元素 39 inline void Initialize()//初始化清空 40 { 41 size=0; memset(val,0,sizeof(val)); 42 memset(key,-1,sizeof(key)); memset(hash,0,sizeof(hash)); 43 } 44 inline void Newhash(int id,int v) {hash[id]=++size; key[size]=v;}//在hash表中添加新元素 45 Data_Analysis &operator [](const int State) 46 { 47 for(int i=State%mod;;i=(i+1==mod)?0:i+1)//不停的通过+1的操作向后找,循环查找,直到找到,通过retrun结束for循环 48 { 49 if(!hash[i]) Newhash(i,State);//没有就添加新元素 50 if(key[hash[i]]==State) return val[hash[i]];//找到该状态,返回该状态下的方案数 51 } 52 } 53 }f[2]; 54 //所有状态均为4进制(以2进制为基础,每两位看作一个整体代表4进制,利用位运算简化操作过程) 55 //0无插头 1左插头 2右插头 56 inline int Find(int State,int id)//查找该处插头种类,id代表所在格子是第几列,所以id=x时对应二进制表示中(两位一整体)第x-1个整体 57 { 58 return (State>>((id-1)<<1))&3;//(id-1)<<1即(id-1)*2,目的是把需要被查找的位置移到第0,1位处,&3会把除最后两位之外的其他位均置0,并且不改变最后两位 59 } 60 inline void Set(int &State,int bit,int val)//修改插头类型 61 { 62 bit=(bit-1)<<1;//由于两位一整体,直接算出当前整体的前一位在哪 63 State|=3<<bit;//先置1 64 State^=3<<bit;//后置0 65 State|=val<<bit;//把当前位置改变为val 66 } 67 inline int Link(int State,int pos)//查找对应的另一个插头在哪 68 { 69 int cnt=0;//标记已经历过的左右插头能否相互抵消,当相互抵消时证明找到了当前插头的对应插头 70 int Delta=(Find(State,pos)==1)?1:-1;//当前是左插头,就向右找,否则向左找 71 for(int i=pos;i&&i<=m+1;i+=Delta)//有可能向左,有可能向右,所以要保证1<=i<=m+1(插头编号由1到m+1) 72 { 73 int plug=Find(State,i); 74 if(plug==1) cnt++; 75 else if(plug==2) cnt--; 76 if(cnt==0) return i;//一一对应证明找到了相对的左/右插头 77 } 78 return -1;//扫了一遍没找到 79 } 80 inline void Execution(int x,int y) 81 { 82 //((x-1)*m+y)求出是第几个格 83 int now=((x-1)*m+y)&1;//now只有可能=0/1,只保留最后一位 84 int last=now^1;//0^1=1 1^1=0记录连着的上一个格子,类似于滚动数组只用0/1 85 int tot=f[last].size;//上一个格子共有tot种不同状态 86 f[now].Initialize();//给当前格子清零 87 for(int i=1;i<=tot;i++) 88 { 89 int State=f[last].key[i];//枚举决策上一个格子时的所有不同状态 90 Data_Analysis Val=f[last].val[i];//状态所对应的方案数 91 int plug1=Find(State,y),plug2=Find(State,y+1);//寻找当前决策的格子上的两个插头的种类 92 if(Link(State,y)==-1||Link(State,y+1)==-1) continue;//没有对应的插头 93 if(!plug1&&!plug2)//没有插头 94 { 95 if(x!=n&&y!=m) {Set(State,y,1); Set(State,y+1,2); f[now][State]+=Val;}//只要不是最后一个格子,就建下插头及右插头,作为1插头和2插头 96 } 97 else if(plug1&&!plug2)//只有一个来自左边的插头 98 { 99 if(x!=n) f[now][State]+=Val;//转弯,连接一个下插头 100 if(y!=m) {Set(State,y,0); Set(State,y+1,plug1); f[now][State]+=Val;}//直走,向右 101 } 102 else if(!plug1&&plug2)//只有一个来自上面的插头 103 { 104 if(y!=m) f[now][State]+=Val;//转弯,连接一个右插头 105 if(x!=n) {Set(State,y,plug2); Set(State,y+1,0); f[now][State]+=Val;}//直走,向下 106 } 107 else if(plug1==1&&plug2==1)//两个左插头,把靠里的那个左插头的右插头变为左插头,两个插头联通,置为没有 108 {Set(State,Link(State,y+1),1); Set(State,y,0); Set(State,y+1,0); f[now][State]+=Val;} 109 else if(plug1==1&&plug2==2)//左边是左插头,右边是右插头 110 {if(x==n&&y==m) Ans+=Val;}//如果是最后一个格子,直接封口 111 else if(plug1==2&&plug2==1)//左边是右插头,右边是左插头,没影响,直接判联通,置为0,对对应插头无影响 112 {Set(State,y,0); Set(State,y+1,0); f[now][State]+=Val;} 113 else if(plug1==2&&plug2==2)//两个右插头,联通置为0,靠里的左插头变右 114 {Set(State,Link(State,y),2); Set(State,y,0); Set(State,y+1,0); f[now][State]+=Val;} 115 } 116 } 117 int main() 118 { 119 scanf("%d%d",&n,&m); 120 if(m>n) swap(n,m);//用较小数状压 121 f[0].Initialize(); f[0][0]=1; 122 for(int i=1;i<=n;i++) 123 { 124 for(int j=1;j<=m;j++) Execution(i,j); 125 if(i!=n)//行间转移 126 { 127 int now=(i*m)&1,tot=f[now].size; 128 for(int j=1;j<=tot;j++) f[now].key[j]<<=2;//两个一整体 129 } 130 } 131 Ans+=Ans; Ans.Print(); 132 }