[bzoj]noip十连测欧贝里斯克的巨神兵(obelisk)——dag图DP,状态压缩

本文探讨了一个有向图中无环子图(DAG)的计数问题,通过动态规划与容斥原理结合的方法,解决计算重叠方案的问题,并提供了一段C++代码实现。

题目大意:
有一张n个点,m条边的有向图,有多少个子图(选定一个边集)是没有环的。答案对1e9+7取模。(n≤17)

显然求的就是DAG个数,于是考虑一层一层的加点,但是发现显然会算重,因为并不能保证层次分明,可能有两个点为同一层的但是又分两层DP导致算重。然后我就想保存上一层的点,那么选入的新点一定要和上一层的点连边才行,这样虽然不会算重,但是不仅要打恶心的3进制DP还是一个O(4^n)的算法???? 对于这种转移顺序导致的算重,可以考虑容斥原理,具体来说,我们现在不考虑层,就一个一个的加点,那么先加A后加B和先加B后加A计算了两次,所以要剪掉AB同时被加的方案,后面同理容斥就行。。。。。。

AC Code:

#include<bits/stdc++.h>
#define maxn 17
#define mod 1000000007
using namespace std;

int n,m;
int g[maxn][maxn],bit[1<<maxn],ways[1<<maxn],way2[maxn][1<<maxn];
int dp[1<<maxn],P2[301],lg[1<<maxn];

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),u--,v--,g[u][v]++;
	bit[0] = -1;
	for(int i=1;i<(1<<n);i++) bit[i] = -bit[i - (i & -i)];
	P2[0]=1;for(int i=1;i<=300;i++) P2[i] = 2ll * P2[i-1] % mod;
	for(int i=0;i<maxn;i++) lg[1<<i] = i;
	
	dp[0] = 1;
	for(int i=0;i<(1<<n);i++)
	{
		int rsta = (1<<n) - 1 - i , las = lg[i & -i];
		if(i)
			for(int j=0;j<n;j++) 
				ways[1<<j] = way2[j][i] = way2[j][i-(1<<las)] + g[las][j];
		if(rsta) for(int j=rsta & (rsta - 1);;j = (j-1) & rsta)
		{
			int now = j ^ rsta , res = now & -now;
			ways[now] = ways[now - res] + ways[res];
			dp[now | i] = (dp[now | i] + 1ll * bit[now] * dp[i] * P2[ways[now]]) % mod;
			if(!j) break;
		}
	}
	
	printf("%d\n",dp[(1<<n)-1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值