zoukankan      html  css  js  c++  java
  • hash表及带注释插头dp

    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
    ");
            else printf("cnt=%d
    ",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("
    ");
        }
        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
    ",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
    ");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();
    }
    我已没有下降的余地
  • 相关阅读:
    浅析深度优先和广度优先遍历实现过程、区别及使用场景
    浅析为什么要用setTimeout模拟setInterval
    app弹出软键盘获取键盘高度不准确的原因及导致底部定位的元素无法贴近键盘的问题
    App平台iOS设备上因内存不足导致白屏、闪退的原因及其解决方案
    浅谈移动端开发技术
    浅析Console命令调试常用方法
    js正则表达式中的正向肯定预查和正向否定预查, 反向肯定和反向否定(这个翻译不准确)
    javascript的版本查看及js的历史
    【转】JS-正则表达式的反向引用
    【转】Linux虚拟网络设备之tun/tap
  • 原文地址:https://www.cnblogs.com/znsbc-13/p/11259202.html
Copyright © 2011-2022 走看看