BZOJ 3812 主旋律 状压DP+容斥原理

来源:互联网 发布:巴黎地铁游客数据 编辑:程序博客网 时间:2024/05/18 03:49

题目大意:给定一张有向图,求这张有向图的生成子图中有多少强连通图

正着做不好做,我们考虑容斥原理

如果一个图不连通,那么这张图缩点之后一定会形成一个点数>=2的DAG

一个DAG中一定会有一些入度为0的点,我们枚举这些点的点集进行容斥

具体DP方程和细节见代码 注释写的还是比较详细的我就不多说了= =

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define M 16#define MOD 1000000007using namespace std;int n,m,digit[1<<8];int into[1<<15],out_of[1<<15];long long f[1<<15],g[1<<15],h[1<<15];long long power_2[M*M];/*f[S]表示点集S的生成子图强联通的方案数g[S]表示点集S的生成子图G中,若G的所有联通块都强联通,则G对g[S]存在一个贡献如果G中有奇数个连通块,则对g[S]的贡献为+1,否则为-1h[S]表示点集S的诱导子图中有多少条边f[S]=2^h[S]-Σ[T是S的非空子集]2^cnt*g[T]其中cnt=|{x->y|x∈S,y∈S-T}| (注意此时的g[S]不包含整个S强联通的情况)*/int Count(int x){return digit[x>>8] + digit[x&255] ;}int main(){int i,j,x,y;cin>>n>>m;for(i=1;i<1<<8;i++)digit[i]=digit[i>>1]+(i&1);for(power_2[0]=1,i=1;i<=m;i++)power_2[i]=(power_2[i-1]<<1)%MOD;for(i=1;i<=m;i++){scanf("%d%d",&x,&y);out_of[1<<x-1]|=1<<y-1;into[1<<y-1]|=1<<x-1;}for(i=1;i<1<<n;i++){int one=i&-i,sta=i^one;//one为S集合中任意一点//sta为S集合除掉one外剩余的点集h[i]=h[sta]+Count(into[one]&sta)+Count(out_of[one]&sta);for(j=sta;j;(--j)&=sta)//枚举与one不连通的点集 (g[i]+=MOD-f[i^j]*g[j]%MOD)%=MOD;static int w[1<<15];//w[T]代表集合T中的点到集合S-T中的点的连边数量 f[i]=power_2[h[i]];for(j=i;j;(--j)&=i)//枚举T集合{if(j==i)w[j]=0;else{int temp=(i^j)&-(i^j);//任选S-T集合中的一点w[j]=w[j^temp]-Count((i^j)&out_of[temp])+Count(j&into[temp]);}(f[i]+=MOD-power_2[h[i^j]+w[j]]*g[j]%MOD)%=MOD;}(g[i]+=f[i])%=MOD;}cout<<f[(1<<n)-1]<<endl;return 0;}


2 0
原创粉丝点击