ural1519-Formula 1

题意

给出一个 \(n\times m\) 的棋盘,上面有一些格子是不能经过的。求有多少种欧拉回路可以经过所有可经过到格子。\(n,m\le 12\)

分析

上个月就看了一下插头dp,然而这道题写不出来。现在来看其实也非常好写,只要把情况讨论清楚,对插头dp理解好就可以了。

我们要求的是欧拉回路,只能有一个环,所以要记录连通性状态,而不仅仅是像 hdu1693 这题一样,只记录轮廓线上是否有插头。

\(f(i,j,S)\) 表示转移到 \((i,j)\) 这个格子,轮廓线上方到格子全部被覆盖到,轮廓线状态为 \(S\) 的方案数,考虑如何表示状态 \(S\)

观察轮廓线上方的连通性,可以发现,任意两条线都是不相交的,所以插头的匹配情况可以用一个合法的括号序列来表示。因此我们使用三进制表示法,0='',1='(',2=')' 。接下来讨论各种情况。

下面的内容自己画一画图,讨论一下就可以得出了。

将轮廓线从 1 到 \(m+1\) 标号,那么若当前为 \((i,j)\) ,那么此处的下插头标号为 \(j\) ,这个值设为 \(p\) ,右插头标号为 \(j+1\) ,这个值设为 \(q\)\((i,j-1)\) 的左插头标号为 \(j\) ,这个值设为 \(x\),上插头标号为 \(j+1\) ,值设为 \(y\)

  • 此处是洞,那么只从 \(x=y=0\) 转移到 \(p=q=0\) 。否则:

  • \(x=y=0\) ,转移到 \(p=1,q=2\) ,新建一个连通分量
  • \(x=y=1\) ,转移到 \(p=q=0\)\(y\) 对应的右括号改为左括号
  • \(x=y=2\) ,转移到 \(p=q=0\)\(x\) 对应的左括号改为右括号
  • \(x,y\) 中只有一个为 0,转移到 \(p=x+y,q=0\)\(p=0,q=x+y\) ,延续之前到路线或者在此处转弯
  • \(x=1,y=2\) ,这是对当前的整个连通分量的末端合并,由于我们只能有一条回路,所以这个转移只能在棋盘的最后一个可走位置转移,\(p=q=0\)
  • \(x=2,y=1\) ,合并两个连通分量,\(p=q=0\)

讨论就这样啦!

写法上来说,用数组 \(f,g\) 滚动进行dp,用四进制数来存三进制状态,但这样直接开数组会爆炸,而且其中还有很多没有用的状态(不合法的括号序列),所以一开始我们dfs预处理一下状态,并给它们标号,用一个 unordered_map 来存状态到标号的对应。总的状态数上界大概是 \(\frac{\binom {n} {\frac{n}{2}}}{n+1}*2^\frac{n}{2}*(n+1)\) ,大约为 \(1.1\times 10^5\) ,其实是一个比较松的上界。(卡特兰数为合法括号序列个数,删掉其中的某一些对,最后还有一个位置可以放空。其实还应该容斥空的位置,不过这是一个上界而已)。

复杂度为 \(O(nm|S|)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long giant;
const int maxn=14;
const int maxg=1.2e5;
unordered_map<int,int> id;
int h[maxg],ids=0,a[maxn],mat[maxg][maxn],fx,fy,n,m;
giant f[maxg],g[maxg];
bool ok[maxn][maxn];
inline void match(int mt[]) {
    static int sta[maxn];
    int top=0;
    for (int i=1;i<=m+1;++i) if (a[i]==1) sta[++top]=i; else if (a[i]==2) {
        int x=sta[top--];
        mt[x]=i;
        mt[i]=x;
    }
}
inline bool legal() {
    int tot=0;
    for (int i=1;i<=m+1;++i) if ((tot+=(!a[i]?0:(a[i]==1?1:-1)))<0) return false;
    if (tot) return false;
    return true;
}
inline int at() {
    int ret=0;
    for (int i=m+1;i;--i) (ret+=a[i])<<=2;
    return ret;
}
void dfs(int now) {
    if (now>m+1) {
        if (legal()) {
            int x=at();
            h[id[x]=++ids]=x; // some id is empty!! it equals zero
            match(mat[ids]);
        }
        return;
    }
    for (int i=0;i<3;++i) a[now]=i,dfs(now+1);
}
inline int get(int x,int p) {
    return (x>>(p<<1))&3;
}
inline int mod(int x,int p,int d) {
    int tmp=d<<(p<<1),aux=3<<(p<<1);
    return (x&(~aux))+tmp;
}
vector<int> dec(int x) {
    vector<int> ret;
    ret.clear();
    for (int i=1;i<=m+1;++i) ret.push_back((x>>(i<<1))&3);
    return ret;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    scanf("%d%d",&n,&m);
    dfs(1);
    for (int i=1;i<=n;++i) {
        static char s[maxn];
        scanf("%s",s+1);
        for (int j=1;j<=m;++j) if (s[j]=='.') {
            ok[i][j]=true;
            fx=i,fy=j;
        }
    }
    f[id[0]]=1;
    for (int i=1;i<=n;++i) {
        swap(f,g);
        memset(f,0,sizeof f);
        for (int j=1;j<=ids;++j) {
            int d=h[j];
            if (get(d,m+1)==0) f[id[d<<2]]=g[j];
        }
        for (int j=1;j<=m;++j) {
            swap(f,g);
            memset(f,0,sizeof f);
            for (int k=1;k<=ids;++k) {
                int d=h[k],x=get(d,j),y=get(d,j+1);
                if (!ok[i][j]) {
                    if (!x && !y) f[k]+=g[k];
                } else {
                    if (x==0 && y==0) {
                        int v=mod(mod(d,j,1),j+1,2);
                        int w=id[v];
                        f[w]+=g[k];
                    } else if (x==1 && y==1) {
                        int v=mod(mod(d,j,0),j+1,0);
                        v=mod(v,mat[k][j+1],1);
                        int w=id[v];
                        f[w]+=g[k];
                    } else if (x==2 && y==2) {
                        int v=mod(mod(d,j,0),j+1,0);
                        v=mod(v,mat[k][j],2);
                        int w=id[v];
                        f[w]+=g[k];
                    } else if (x==0 || y==0) {
                        int v1=mod(mod(d,j,x+y),j+1,0);
                        int v2=mod(mod(d,j,0),j+1,x+y);
                        int w1=id[v1],w2=id[v2];
                        f[w1]+=g[k],f[w2]+=g[k];
                    } else if (x==1 && y==2) {
                        if (i==fx && j==fy) {
                            int v=mod(mod(d,j,0),j+1,0);
                            int w=id[v];
                            f[w]+=g[k];
                        }
                    } else if (x==2 && y==1) {
                        int v=mod(mod(d,j,0),j+1,0);
                        int w=id[v];
                        f[w]+=g[k];
                    }
                }
            }
        }
    }
    printf("%lld\n",f[id[0]]);
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/7476771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值