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. Nginx如何配置Http、Https、WS、WSS?

    写在前面 当今互联网领域,Nginx是使用最多的代理服务器之一,很多大厂在自己的业务系统中都是用了Nginx作为代理服务器.所以,我们有必要了解下Nginx对于Http.Https.WS.WSS的各项 ...

  2. vuex、localStorage、sessionStorage之间的区别

    vuex存储在内存中,localStorage以文件形式存储在本地,sessionStorage针对一个session(阶段)进行数据存储. 当页面刷新时vuex存储的数据会被清除,localStor ...

  3. java面试一日一题:如何设计一款垃圾回收器

    问题:如果让你设计一个垃圾回收器,你会考虑哪些问题 分析:该问题主要考察对java中垃圾回收器的理解,要理解怎么回收:一款好的垃圾回收器有哪些衡量指标 回答要点: 主要从以下几点去考虑, 1.垃圾回收 ...

  4. Markdown使用概述

    Markdown使用概述 序言 作为一名编程学习的爱好者和初学者,由于学习编程的过程中总是存在遗忘以及很难动手写起来的问题,所以在看了许多关于编程学习方法的文章之后,选择使用typora作为我的笔记工 ...

  5. head元素的内容

    一.head元素 head元素的内容用来向浏览器提供一些文档信息,此外还可以包含js脚本和css层叠样式单.head中一般包含title.meta.css.js等内容,head中元素的内容在浏览器中不 ...

  6. Linux中find命令用法全汇总,看完就没有不会用的!

    Linux中find命令用法全汇总,看完就没有不会用的! 中琦2513 马哥Linux运维 2017-04-10   糖豆贴心提醒,本文阅读时间7分钟 Linux 查找命令是Linux系统中最重要和最 ...

  7. du -cs /var/lib/BackupPC/pc/10.1.60.211/目录名

    # du -cs /var/lib/BackupPC/pc/10.1.60.211/7870236 /var/lib/BackupPC/pc/10.1.60.211/7870236 总用量  

  8. Linux软件安装管理之——dpkg与apt-*详解 apt命令(dpkg和apt代替rpm)

    apt list *python* Nosee123关注赞赏支持   Linux软件安装管理之--dpkg与apt-*详解 [Linux软件安装管理系列]- - 传送门: - -<Linux软件 ...

  9. Ansible_常用文件模块使用详解

    一.Ansibel常用文件模块使用详解 1.file模块 1️⃣:file模块常用的参数列表: path       被管理文件的路径 state状态常用参数: absent           删除 ...

  10. 【备忘】ffmpeg推流命令

    1 组播推流 ffmpeg -re -i "D:\\OTT\\adghg2323.mp4" -vcodec libx264 -vprofile baseline -level 30 ...