struct hash_map
{
node s[SZ+10];int e,adj[SZ+10];
inline void init(){e=0;memset(adj,0,sizeof(adj));}
inline void update(LL state,int val,int cnt)
{
RG int i,pos=(state%SZ+(LL)val*SZ)%SZ;
for(i=adj[pos];i&&(s[i].state!=state||s[i].val!=val);i=s[i].next);
if(!i)s[++e].state=state,s[e].val=val,s[e].cnt=cnt,s[e].next=adj[pos],adj[pos]=e;
else s[i].cnt=(s[i].cnt+cnt)%mod;
} inline void find(LL state,int val)
{
RG int i,pos=(state%SZ+(LL)val*SZ)%SZ;
for(i=adj[pos];i&&(s[i].state!=state||s[i].val!=val);i=s[i].next);
if(!i)printf("no such val\n");
else printf("cnt=%d\n",s[i].cnt);
} }f[2];
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 1100000
#define mod 299989
#define P 8
#define N 100000000
ll n,m;
inline ll find(ll state,ll id){
return (state>>((id-1)<<1))&3;
}//看当前插头究竟是什么插头
//因为是四进制每两位代表一个状态
struct bignum
{
ll n[10],l;
bignum(){l=1,memset(n,0,sizeof(n));}
void clear(){while(l>1&&!n[l-1]) l--;}
void print(){
printf("%lld",n[l-1]);
for(ll i=l-2;i>=0;i--)
printf("%0*lld",P,n[i]);
printf("\n");
}
bignum operator +(bignum x)const{
bignum t=*this;
if(t.l<x.l) t.l=x.l;
t.l++;
for(ll i=0;i<t.l;i++){
t.n[i]+=x.n[i];
if(t.n[i]>=N){
t.n[i+1]+=t.n[i]/N;
t.n[i]%=N;
}
}
t.clear();
return t;
}
bignum operator =(ll x){
l=0;
while(x){
n[l++]=x%N;
x/=N;
}
return *this;
}
inline void operator +=(bignum b){*this=*this+b;}
}Ans;
struct hash_map
{
bignum val[mod];
ll key[A],hash[mod],size;
inline void init(){
memset(val,0,sizeof(val));
memset(key,-1,sizeof(key));size=0;
memset(hash,0,sizeof(hash));
}
inline void newhash(ll id,ll v){hash[id]=++size;key[size]=v;}
bignum &operator [] (const ll &state){
for(ll i=state%mod;;i=(i+1==mod)?0:i+1)
{
if(!hash[i]) newhash(i,state);
if(key[hash[i]]==state) return val[hash[i]];
}
}
}f[2];
inline void Set(ll &state,ll bit,ll val){
bit=(bit-1)<<1;
state|=3<<bit;
state^=3<<bit;
//把state高位先赋成0再把它赋成别的
state|=val<<bit;
//state表示状态
//因为插头的编号为0--m所以bit要-1
//因为是四进制,所以3<<
//全都是4进制
//2<<bit
//1<<bit
//貌似还能理解
//每两位代表一个具体状态
}
ll link(ll state,ll pos){
//找到对应的插头(用括号匹配的方式)然后
ll cnt=0,delta=(find(state,pos)==1)?1:-1;
//如果是左括号应该向右寻找右括号
//如果是右括号应该向左寻找左括号
for(ll i=pos;i&&i<=m+1;i+=delta)//一共m+1个插头
{
ll plug=find(state,i);
if(plug==1)
cnt++;//左括号数量++
else if(plug==2)
cnt--;//右括号数量++
if(cnt==0)//当左括号数量与右括号数量相等时找到匹配
return i;//找到了与当前插头对应的插头
}
return -1;
//当前状态是非法的找不到与之对应的插头
}
inline void education(ll x,ll y){
ll now=((x-1)*m+y)&1,last=now^1,tot=f[last].size;
f[now].init();
for(ll i=1;i<=tot;i++){
// printf("i=%lld\n",i);
ll state=f[last].key[i];//key状态
bignum Val=f[last].val[i];//取出上一次权值(方案数)
ll plug1=find(state,y),plug2=find(state,y+1);
//0--m编号,寻找轮廓线上编号y-1,y对应的插头
//至于为什么是y y+1,因为在上面函数里进行了减1
//编号为y-1是左右插头,y代表上下插头
if(link(state,y)==-1||link(state,y+1)==-1)
continue;
//当前括号无法找到匹配无解
if(!plug1&&!plug2){
if(x!=n&&y!=m){
//如果没有插头,直接拽过来两个插头相连(此题保证必须连通)
Set(state,y,1);
//在轮廓线上位置为y-1添加一个左括号
Set(state,y+1,2);
//y位置添加一个右括号
f[now][state]+=Val;
}
}
else if(plug1&&!plug2){
//拥有左右插头没有上下插头
//两种转移方式,转弯向下走
//这样插头状态不变
if(x!=n)
f[now][state]+=Val;
//向右连接一个插头
//向右推进要发生改变
if(y!=m){
Set(state,y,0);
Set(state,y+1,plug1);
f[now][state]+=Val;
}
}
else if(!plug1&&plug2){
//拥有上下插头而没有左右插头
//两种转移方式,向右转移
//这样插头状态不变
if(y!=m)
f[now][state]+=Val;
//向右连接一个插头
if(x!=n){
Set(state,y,plug2);
Set(state,y+1,0);
//plug2是左右插头让上下方向转弯
f[now][state]+=Val;
}
}
else if(plug1==1&&plug2==1){
//两个左括号插头相连接,然后让最靠左的右括号插头变成左括号插头
Set(state,link(state,y+1),1);
Set(state,y,0);
Set(state,y+1,0);
f[now][state]+=Val;
}
else if(plug1==1&&plug2==2){
//右插头是左括号插头,上插头是右括号插头,连在一起
//构成回路
if(x==n&&y==m)
Ans+=Val;
}
else if(plug1==2&&plug2==1){
//无脑合并
Set(state,y,0);
Set(state,y+1,0);
f[now][state]+=Val;
}
else if(plug1==2&&plug2==2){
//当你有左右插头右括号插头,上下插头为右插头
//合并为1,将最靠右左插头变为右插头
Set(state,link(state,y),2);
Set(state,y,0);
Set(state,y+1,0);
f[now][state]+=Val;
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
if(n==1||m==1){printf("1\n");return 0;}
if(m>n) swap(n,m);
f[0].init();f[0][0]=1;
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=m;j++)
education(i,j);
if(i!=n){
ll now=(i*m)&1,tot=f[now].size;
for(ll j=1;j<=tot;j++)
f[now].key[j]<<=2;
}
}
Ans+=Ans;
Ans.print();
}

hash表及带注释插头dp的更多相关文章

  1. BZOJ.1210.[HNOI2004]邮递员(插头DP Hash 高精)

    BZOJ 洛谷 http://www.cnblogs.com/LadyLex/p/7326874.html 插头DP.\(m+1\)个插头的状态需要用三进制表示:\(0\)表示无插头,\(1\)表示是 ...

  2. SQLServer之数据库表转化为实体类【带注释】

    1.在开发过程中,有时候需要将数据库表转化为实体类.手敲除了不方便,还容易出错.本着DRY+懒人原则,参考了一位老司机的博客[见底部],并在其基础上进行了优化.[原先是不带注释的] DECLARE @ ...

  3. 四种方式带你层层递进解剖算法---hash表不一定适合寻找重复数据

    一.题目描述 找出数组中重复的数字 > 在一个长度为 n 的数组 nums 里的所有数字都在 0-n-1 的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次. ...

  4. 插头dp小结

    插头dp: \(A:\)插头dp是什么? \(B:\)一种基于连通性状态压缩的动态规划问题 \(A:\)请问有什么应用呢? \(B:\)各种网格覆盖问题,范围允许状压解决,常用于计算方案数与联通块权值 ...

  5. 【Learning】插头DP

    简介 插头DP(轮廓线DP)是用来解决网格图回路问题的一种算法. 插头DP解决的经典问题就是统计经过所有格子的哈密顿回路条数,某些格子有障碍. ​ 如果问题稍微进阶一点的话,不一定要求路径是回路.路径 ...

  6. HDU 4113 Construct the Great Wall(插头dp)

    好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...

  7. HDU 4949 Light(插头dp、位运算)

    比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...

  8. 插头DP专题

    建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...

  9. HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)

    插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...

随机推荐

  1. java 运用Cipher加密再解密后会变乱码。解决方案!

    同样的方法类用main调用加解密都正常,就是当用到业务就是加密后再解密变乱码. 后来发现同样的内容加密后的内容竟不相同. 经调试发现 encryptData.getBytes() 转为字节是的使用 C ...

  2. 使用DevExpress的GridControl实现多层级或无穷级的嵌套列表展示

    在我早期的随笔<在GridControl表格控件中实现多层级主从表数据的展示>中介绍过GridControl实现二级.三级的层级列表展示,主要的逻辑就是构建GridLevelNode并添加 ...

  3. 偷梁换柱:使用mock.patch辅助python单元测试

    最近在搞软工项目的后端测试,重新复习了一下python的mock.patch,并用它简化了对一些复杂逻辑的测试,在此记录 问题描述 本组的项目比较特殊,设计对教务网站的模拟登陆与信息爬取,同时不少接口 ...

  4. opencv——轮廓发现与轮廓(二值图像)分析

    引言 二值图像分析最常见的一个主要方式就是轮廓发现与轮廓分析,其中轮廓发现的目的是为轮廓分析做准备,经过轮廓分析我们可以得到轮廓各种有用的属性信息. 这里顺带提下边缘检测,和轮廓提取的区别: 边缘检测 ...

  5. MongoDB&#183;Windows下管理员密码重置解决方案

    阅文时长 | 1.07分钟 字数统计 | 1730.4字符 主要内容 | 1.问题切入 2.详细步骤 3.声明与参考资料 『MongoDB·Windows下管理员密码重置解决方案』 编写人 | SCs ...

  6. [c++] 模板、迭代器、泛型

    模板 函数模板:重载的进一步抽象,只需定义一个函数体即可用于所有类型 在C++中,数据的类型也可以通过参数来传递,在函数定义时可以不指明具体的数据类型,当发生函数调用时,编译器可以根据传入的实参自动推 ...

  7. ruby基础(三)

    类和模块 1.类 类是面向对象中一个重要的术语.我们可以把类看作是对象的抽象, 所有的这类对象都有这些特征.而对象则是类的具体实现,按照类的要求创建的 对象就是该类的对象.类就像对象的雏形一样,决定了 ...

  8. 消息队列之 RabbitMQ【验证通过】

    消息队列之 RabbitMQ 预流 关注  22.9 2017.05.06 16:03* 字数 4884 阅读 284691评论 41喜欢 618赞赏 2 关于消息队列,从前年开始断断续续看了些资料, ...

  9. 分布式存储ceph---ceph osd 故障硬盘更换(6)

    正常状态: 故障状态: 实施更换步骤: 1.关闭ceph集群数据迁移: osd硬盘故障,状态变为down.在经过mod osd down out interval 设定的时间间隔后,ceph将其标记为 ...

  10. 在 Ubuntu 上安装 .NET SDK 或 .NET 运行时

    在wsl Ubuntu 20.04上面安装dotnet链接 https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-ubuntu Ubun ...