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

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

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

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

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

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

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 16
#define MOD 1000000007
using 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,否则为-1
h[S]表示点集S的诱导子图中有多少条边

f[S]=2^h[S]-Σ[T是S的非空子集]2^(h[S]-h[T])*g[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;
}
时间: 2024-11-05 20:48:05

BZOJ 3812 主旋律 状压DP+容斥原理的相关文章

【uoj#37/bzoj3812】[清华集训2014]主旋律 状压dp+容斥原理

题目描述 求一张有向图的强连通生成子图的数目对 $10^9+7$ 取模的结果. 题解 状压dp+容斥原理 设 $f[i]$ 表示点集 $i$ 强连通生成子图的数目,容易想到使用总方案数 $2^{sum[i]}$ 减去不为强连通图的方案数得到强连通图的方案数,其中 $sum[i]$ 表示点集 $i$ 中边的数目. 考虑什么样的图不是强连通图:缩点后入度为0的强连通分量对应的点集不是全集. 枚举这些入度为0的强连通分量对应的点集,由于无法保证只有这些点构成的入度为0的强连通分量,因此需要进一步容斥.

BZOJ 2669 cqoi2012 局部极小值 状压DP+容斥原理

题目大意:给定一个n?m的矩阵,标记出其中的局部极小值,要求填入1...n?m,求方案数 <多年的心头大恨终于切掉了系列> 考虑将数字从小到大一个一个填进去 由于局部极小值最多8个,我们可以状压DP 令fi,j表示已经填完了前i个数,局部极小值的填充状态为j的方案数 预处理出cntj表示填充状态为j时共有多少位置是可以填充的(包括已填充的局部极小值位置) 那么有DP方程fi,j=fi?1,j?C1cntj?i+1+∑k∈jfi?1,j?{k} 但是问题是这样虽然保证了标记的位置都是局部最小值,

BZOJ 1072 排列 状压DP

题意:链接 方法:状压DP? 题解:这题其实没啥好写的,不算很难,推一推就能搞出来. 首先看到这个问题,对于被d整除这个条件,很容易就想到是取余数为0,所以想到可能状态中刚开始含有取余数. 先说我的第一个想法,f[i][j]表示选取i个数且此时的mod为j,这样的思想是第一下蹦出来的,当时想的就是在线来搞最终的答案.不过转瞬即发现,这TM不就是暴力吗魂淡!并没有什么卵用,于是开始想这个状态可不可以做什么优化. 显然第二维的j并不需要太大的优化,暂且先将其搁置一边,来考虑第一维的i怎么优化. 把滚

4455: [Zjoi2016]小星星|状压DP|容斥原理

OrzSDOIR1ak的晨神 能够考虑状压DP枚举子集,求出仅仅保证连通性不保证一一相应的状态下的方案数,然后容斥一下就是终于的答案 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #include<queue>

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6

[BZOJ 1072] [SCOI2007] 排列perm 【状压DP】

题目链接:BZOJ 1072 这道题使用 C++ STL 的 next_permutation() 函数直接暴力就可以AC .(使用 Set 判断是否重复) 代码如下: #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <set>

BZOJ 1087: [SCOI2005]互不侵犯King( 状压dp )

简单的状压dp... dp( x , h , s ) 表示当前第 x 行 , 用了 h 个 king , 当前行的状态为 s . 考虑转移 : dp( x , h , s ) = ∑ dp( x - 1 , h - cnt_1( s ) , s' ) ( s and s' 两行不冲突 , cnt_1( s ) 表示 s 状态用了多少个 king ) 我有各种预处理所以 code 的方程和这有点不一样 ------------------------------------------------

BZOJ 3446: [Usaco2014 Feb]Cow Decathlon( 状压dp )

水状压dp. dp(x, s) = max{ dp( x - 1, s - {h} ) } + 奖励(假如拿到的) (h∈s). 时间复杂度O(n * 2^n) ---------------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x

BZOJ 1087 题解【状压DP】

1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3112  Solved: 1816[Submit][Status][Discuss] Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K &