【BZOJ】3812: 主旋律-DP&容斥

传送门:bzoj3812


题解

GXZ的题解

神仙容斥给跪了orz。

f [ S ] f[S] f[S]表示点集 S S S强连通生成子图的方案数(即答案), s u m [ S ] sum[S] sum[S]表示点集 S S S中的有向边数,考虑容斥:用总方案数 2 s u m [ S ] 2^{sum[S]} 2sum[S]减去所有非强连通图的方案数。

枚举非强连通图的方法很巧妙:

若缩点后入度为0的 s c c scc scc点集不为全集,则图不强连通。那么可以枚举缩点后所有入度为0的 s c c scc scc的点集 T T T,设 g ′ [ T ] g'[T] g[T]表示上述点集为 T T T的方案数(即一些 s c c scc scc,且 s c c scc scc间无连边)。设 S S S中指向子集 T T T的边数量为 w [ S ] [ T ] w[S][T] w[S][T],贡献为 2 s u m [ S ] − w [ S ] [ T ] × g ′ [ T ] 2^{sum[S]-w[S][T]}\times g'[T] 2sum[S]w[S][T]×g[T]

但这样不能保证 S − T S-T ST中没有入度为0的 s c c scc scc,所以需要进一步容斥,奇数个连通块容斥系数为正,偶数个为负(类似于至少有多少个),设 g [ T ] g[T] g[T]表示点集 T T T形成奇数个连通块的方案数-点集 T T T形成偶数个连通块的方案数。
g [ S ] = − ∑ T ⊂ S , l o w b i t ( S ) ∈ T f [ T ] g [ S − T ] g[S]=-\sum \limits_{T\subset S,lowbit(S)\in T}f[T]g[S-T] g[S]=TS,lowbit(S)Tf[T]g[ST]

综上 f [ S ] = 2 s u m [ S ] − ∑ T ⊂ S 2 s u m [ S ] − w [ S ] [ T ] g [ T ] f[S]=2^{sum[S]}-\sum\limits_{T\subset S}2^{sum[S]-w[S][T]}g[T] f[S]=2sum[S]TS2sum[S]w[S][T]g[T]
p.s.需要在同步更新完 f [ S ] , g [ S ] f[S],g[S] f[S],g[S] f [ S ] f[S] f[S]计入 g [ S ] g[S] g[S](实际上在转移的时候, g [ S ] g[S] g[S]是没有算 s c c scc scc为全集的情况的)


代码

#include
using namespace std;
typedef long long ll;
const int N=15,M=(1<<15)+10,mod=1e9+7;

int S,n,m,in[M],ot[M],bin[300],stk[M],top;
int f[M],g[M],sum[M],w[M],sz[M];

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}

int main(){
	int i,j,k,x,y;bin[0]=1;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i){
		scanf("%d%d",&x,&y);x--;y--;
		in[1<<y]|=1<<x;ot[1<<x]|=1<<y;
		bin[i]=ad(bin[i-1],bin[i-1]);
	}S=bin[n];
	for(i=1;i<S;++i){
		j=i&(-i);x=i^j;sz[i]=sz[x]+1;
		sum[i]=sum[x]+sz[in[j]&i]+sz[ot[j]&i];
		f[i]=bin[sum[i]];top=0;
		for(j=i;j;j=i&(j-1)) stk[++top]=j;
		for(;top;){k=stk[top--];w[k]=w[k^(k&(-k))]+sz[in[k&(-k)]&i];} 
		for(j=x;j;j=x&(j-1)) g[i]=dc(g[i],(ll)f[i^j]*g[j]%mod);
		for(j=i;j;j=i&(j-1)) f[i]=dc(f[i],(ll)bin[sum[i]-w[j]]*g[j]%mod);
		g[i]=ad(g[i],f[i]);
	}
	printf("%d",f[S-1]);
	return 0;
}

你可能感兴趣的:(计数DP)