hdu4285-circuits

题意

一个 \(n\times m\) 的方格纸,有一些格子不能走。给出一个 \(k\) ,求有多少种方案,用 \(k\) 个不相交,不嵌套 的环覆盖所有可以走的格子。\(n,m\le 12\)

分析

若只有 \(k\) 个环的限制,那把它放进状态里就可以了。关键是如何解决不嵌套问题。我们在一个环形成的时候处理嵌套。若这个环被奇数个插头套着,那它至少会被它外层的那对插头形成的环包含,所以不转移。若是偶数个,那么接下来继续这样进行,就一定不会发生嵌套的情况。

为什么呢?考虑刚刚形成的这个环,外面的那层线,由于这个环被偶数个插头对套着,所以外层的线是被奇数个插头对套着,所以它一定不能成环,那么就会消除外面的两层。剩下的情况是一样的。

于是讨论一下,转移即可。

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

这也算是插头dp棋盘模型的一个结束了。

代码

卡掉了所有的内置 hash_table 。终于手写了一次,因为不想改代码,所以实现了大部分接口,除了 map::iterator 不知道怎么实现,不过也不是很需要。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ui;
typedef long long giant;
const int maxn=14;
const int maxs=3e5;
const ui haq=3e5+7;
const int q=1e9+7;
inline int Plus(int x,int y) {return ((giant)x+(giant)y)%q;}
inline void Pe(int &x,int y) {x=Plus(x,y);}
int n,m,ned;
bool no[maxn][maxn];
struct Hash {
    struct E {
        ui v;
        int w,nxt;
    } e[maxs];
    int h[haq],tot;
    inline void clear() {tot=0,memset(h,0,sizeof h);}
    Hash () {clear();}
    inline int& operator [] (ui x) {
        ui wh=x%haq;
        for (int i=h[wh];i;i=e[i].nxt) if (e[i].v==x) return e[i].w;
        e[++tot]=(E){x,0,h[wh]};
        return e[h[wh]=tot].w;
    }
};
struct Map {
    Hash *hs;
    Map () {hs=new Hash();}
    inline void clear() {hs->clear();}
    inline int& operator [] (ui x) {return (*hs)[x];}
    inline void swap(Map &o) {
        std::swap(hs,o.hs);
    }
} f,g;
void get(Map &g) {
    for (int i=1;i<=g.hs->tot;++i) printf("[%llu]: %d, ",g.hs->e[i].v,g.hs->e[i].w);
    puts("");
}
int mt[maxn];
inline ui get(ui x,int p) {
    if (p==m+2) return x>>((m+2)<<1);
    return (x>>(p<<1))&3;
}
inline ui mod(ui x,int p,ui d) {
    if (p==m+2) {
        ui bef=x&((1u<<((m+2)<<1))-1);
        return bef+(d<<((m+2)<<1));
    }
    return (x&(~(3<<(p<<1))))+(d<<(p<<1));
}
inline void match(ui x,int *mt) {
    static int sta[maxn];
    int top=0;
    for (int i=0;i<maxn;++i) mt[i]=0;
    for (int i=1;i<=m+1;++i) {
        const ui d=get(x,i);
        if (d==1) sta[++top]=i; else if (d==2) {
            int p=sta[top--];
            mt[p]=i,mt[i]=p;
        }
    }
}
void dec(ui x) {
    for (int j=1;j<=m+1;++j) printf("%llu  ",get(x,j));
    printf("%llu\n",get(x,m+2));
}
void work() {
    scanf("%d%d%d",&n,&m,&ned);
    memset(no,0,sizeof no);
    for (int i=1;i<=n;++i) {
        static char s[maxn];
        scanf("%s",s+1);
        for (int j=1;j<=m;++j) no[i][j]=(s[j]=='*');
    }
    if (ned>n*m/4) {
        puts("0");
        return;
    }
    f.clear(),g.clear();
    f[0]=1;
    for (int i=1;i<=n;++i) {
        f.swap(g),f.clear();
        for (int it=1;it<=g.hs->tot;++it) {
            const ui &d=g.hs->e[it].v,s=get(d,m+2);
            const int &w=g.hs->e[it].w;
            if (get(d,m+1)==0) Pe(f[mod(mod(d,m+2,0)<<2,m+2,s)],w);
        }
        for (int j=1;j<=m;++j) {
            f.swap(g),f.clear();
            for (int it=1;it<=g.hs->tot;++it) {
                const ui &d=g.hs->e[it].v,s=get(d,m+2),e=mod(mod(d,j,0),j+1,0),x=get(d,j),y=get(d,j+1);
                const int &w=g.hs->e[it].w;
                match(d,mt);
                if (no[i][j]) {
                    if (x==0 && y==0) Pe(f[d],w);
                    continue;
                }
                if (x==0 && y==0) Pe(f[mod(mod(e,j,1),j+1,2)],w); else
                if (x==0 || y==0) {
                    Pe(f[mod(e,j,x+y)],w);
                    Pe(f[mod(e,j+1,x+y)],w);
                } else if (x==1 && y==1) Pe(f[mod(e,mt[j+1],1)],w);
                else if (x==2 && y==2) Pe(f[mod(e,mt[j],2)],w); 
                else if (x==2 && y==1) Pe(f[e],w); 
                else if (x==1 && y==2) {
                    if (s>=ned) continue;
                    int cnt=0;
                    for (int k=1;k<j;++k) cnt+=(bool)get(d,k);
                    if (~cnt&1) Pe(f[mod(e,m+2,s+1)],w);
                }
            }
        }
    }
    printf("%d\n",f[mod(0,m+2,ned)]);
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    int T;
    scanf("%d",&T);
    while (T--) work();
    return 0;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值