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 }