插头$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 //之前打的板子已经不会了

 

猜你喜欢

转载自www.cnblogs.com/2018hzoicyf/p/12021757.html