题解-bzoj2560 串珠子

Posted penth

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解-bzoj2560 串珠子相关的知识,希望对你有一定的参考价值。

刚被教练数落了一通,心情不好,来写篇题解

Problem

bzoj2560

题目简述:给定(n)个点的,每两个点(i,j)之间有(c_{i,j})条直接相连的路(其中只能选一条或不选),问共有多少种方案可以使得整张图连通。(nleq 16)

Solution

算是遇到的没那么套路的容斥题了 虽然还是有点套路

发现(nleq 16)各种暗示我们要状压,于是按照以往状压的题的套路,设(f(S))表示当(S)集合中的点连通方案数

发现不是很好直接计算,但总方案数又很好得出,于是考虑容斥,设(g(S))表示集合(S)中的点之间随意相连的方案数

根据定义可得

[g(S)=prod_{i,jin S}(c_{i,j}+1)]

想法用(g)去消掉(f)不满足题意的方案数,联想到城市规划中的做法:限定(1)号节点的连通集合大小

类似的,这里可以限定(S)中编号最小的点连通大小(当然编号最大的点也行)

枚举(S)中编号最小的点连通块大小,可以得到(设(H)为集合(S)中去除最小元素的集合):

(f(S)=g(S)-sum_{Tsubseteq H}g(T)f(S-T))

题目之间类比关系好多啊,比如上一篇就是二项堆和AC自动机的类比

Code

#include <cstdio>
const int N=18,M=1<<N,p=1e9+7;
int g[M],f[M],bin[N];
int a[N][N],n;

inline int qm(int x){return x<p?x:x-p;}

int main(){
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    for(int j=0;j<n;++j)
        scanf("%d",&a[i][j]);
    bin[0]=1;
    for(int i=1;i<=n;++i)bin[i]=bin[i-1]<<1;
    for(int S=0,s;S<bin[n];++S){
        f[S]=1;
        for(int i=0;i<n;++i)if(bin[i]&S)
        for(int j=i+1;j<n;++j)if(bin[j]&S)
            f[S]=1ll*f[S]*(a[i][j]+1)%p;
        g[S]=f[S],s=(S-1)&S;
        for(int i=s;i;i=(i-1)&s)
            f[S]=qm((int)f[S]-1ll*g[i]*f[S^i]%p+p);
    }
    printf("%d
",f[bin[n]-1]);
    return 0;
}

以上是关于题解-bzoj2560 串珠子的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2560串珠子 状压dp+容斥(?)

BZOJ2560串珠子 状压DP+容斥

bzoj 2560 串珠子

bzoj 2560 串珠子

bzoj2560 串珠子

BZOJ2560 串珠子