插头$DP$初步
之前学的全忘了,到头来又要重炒一遍板子,不过因为码力增强(认真抄),感觉思路清晰一点了。
邮递员:
放个注释的板子
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #define LL __int128 6 using namespace std; 7 int n,m; 8 LL ans=0; 9 struct HASH_MAP{ 10 #define mo 2601 11 struct nnode{ 12 int nt,key; 13 LL val; 14 }e[mo*20];int hd[mo],itot; 15 void init(){ 16 itot=0; 17 memset(hd,0,sizeof(hd)); 18 return ; 19 } 20 LL &operator [](const int &key){ 21 const int u=key%mo; 22 for(int i=hd[u];i;i=e[i].nt) 23 if(e[i].key==key)return e[i].val; 24 e[++itot].key=key,e[itot].val=0; 25 e[itot].nt=hd[u],hd[u]=itot; 26 return e[itot].val; 27 } 28 }f[2]; 29 int now=1,past=0; 30 inline int Find(int sta,int pos){ 31 return (sta>>((pos-1)<<1))&3; 32 }//四进制取位 33 inline void Set(int &sta,int pos,int w){ 34 sta-=(Find(sta,pos)<<((pos-1)<<1)); 35 sta+=w<<((pos-1)<<1); 36 return ; 37 }//四进制位赋值 38 //0无插头 1左括号插头 2右括号插头 39 inline int Get(int sta,int pos){ 40 int knd=Find(sta,pos); 41 if(knd==0)return pos; 42 //自己是自己来省掉无插头 43 int cnt=0; 44 if(knd==1){//左括号往右找 45 for(int i=pos,tmp;i<=m+1;++i){ 46 tmp=Find(sta,i); 47 if(tmp==1)++cnt; 48 if(tmp==2)--cnt; 49 if(cnt==0)return i; 50 } 51 } 52 else{ 53 for(int i=pos,tmp;i>=1;--i){ 54 tmp=Find(sta,i); 55 if(tmp==1)++cnt; 56 if(tmp==2)--cnt; 57 if(cnt==0)return i; 58 } 59 } 60 return -1; 61 }//查找对应的插头,即括号匹配 62 void wk(int x,int y){//将轮廓线扩过这个格子 63 int lim=f[past].itot; 64 f[now].init(); 65 for(int i=1;i<=lim;++i){ 66 int sta=f[past].e[i].key; 67 LL val=f[past].e[i].val; 68 int p1=Find(sta,y),p2=Find(sta,y+1); 69 //左边的格子的插头,上边格子的插头 70 int g1=Get(sta,y),g2=Get(sta,y+1); 71 if(g1==-1||g2==-1)//没有对应插头,不合法 72 continue; 73 if(!p1&&!p2){//新建两个插头 74 if(x!=n&&y!=m){ 75 Set(sta,y,1); 76 Set(sta,y+1,2); 77 f[now][sta]+=val; 78 } 79 } 80 else if(!p1&&p2){//要么向下,要么向右 81 if(y!=m)f[now][sta]+=val; 82 if(x!=n){ 83 Set(sta,y,p2); 84 Set(sta,y+1,0); 85 f[now][sta]+=val; 86 } 87 } 88 else if(p1&&!p2){//同上 89 if(x!=n)f[now][sta]+=val; 90 if(y!=m){ 91 Set(sta,y,0); 92 Set(sta,y+1,p1); 93 f[now][sta]+=val; 94 } 95 } 96 else if(p1==1&&p2==1){//交换p2和p2对应插头的类型,使p1和p2能拼在一起 97 Set(sta,g2,1); 98 Set(sta,y,0); 99 Set(sta,y+1,0); 100 f[now][sta]+=val; 101 } 102 else if(p1==2&&p2==2){//同上 103 Set(sta,g1,2); 104 Set(sta,y,0); 105 Set(sta,y+1,0); 106 f[now][sta]+=val; 107 } 108 else if(p1==1&&p2==2){ 109 if(x==n&&y==m) 110 ans+=val; 111 } 112 else if(p1==2&&p2==1){//交换p1和p2的类型 113 Set(sta,y,0); 114 Set(sta,y+1,0); 115 f[now][sta]+=val; 116 } 117 } 118 now=past,past^=1; 119 return ; 120 } 121 void pt(LL x){ 122 if(!x)return ; 123 pt(x/10); 124 putchar(x%10+'0'); 125 return ; 126 } 127 int main(){ 128 //freopen("da.in","r",stdin); 129 130 cin>>n>>m; 131 if(n==1||m==1)puts("1"),exit(0); 132 if(m>n)m^=n,n^=m,m^=n; 133 f[0].init();f[1].init(); 134 f[0][0]=1; 135 for(int i=1;i<=n;++i){ 136 for(int j=1;j<=m;++j)wk(i,j); 137 if(i!=n){ 138 int lim=f[past].itot; 139 for(int j=1;j<=lim;++j) 140 f[past].e[j].key<<=2; 141 //转到下一行,多一个位表示新增的墙角 142 } 143 } 144 ans+=ans; 145 //cout<<ans<<endl; 146 if(!ans)puts("0"),exit(0); 147 pt(ans); 148 149 return 0; 150 } 151 //工业化代码应该还是好调一些的 152 //ladylex学长的板子 153 //之前打的板子已经不会了