floj2464

题意:

给出一个n(n<=16)个点m条边的无向图,要求给每条边重定向使得1号点所能到达的点的集合与2号点所能到达的点的集合不为空
求出方案数模1e9+7后的值

题解:

(f_{1/2,S}(S表示一些无向图中相互连通的点形成的集合))表示从1/2节点出发,仅通过S内部边定向,恰能走到S中所有点的方案数
那么我们考虑转移
正难则反。考虑求出所有不合法的方案数,在用总数减去就可以了
即用总数减去只能到达S的某个真子集T的方案数
枚举S的每一个非空真子集T,那么容易写出转移方程
(f_{1/2.S}=2^{E_S}-sum f_{1/2,T}*2^{E_{S/T}})
其中(E_S)表示集合S所包含的边的个数
此时S/T和S之间的边全部由S/T指向T,而S/T内部的边的方向则任意
那么我们考虑最后统计答案
仍然用总数减去不合法数
当从两个起点出发所能到达的点的集合交集为空时就是不合法的,减去即可(设这两个不相交集合为S1,S2)
那么就有
(ans=2^{m}-sum f_{1,S1}*f_{2,S2}*2^{E_{V/s1/s2}})
此题如此
时间复杂度(O(3^n))

code:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7,N=1<<17;
int n,m,cnt[N],pw[300];
int f1[N],f2[N];
bool G[N];
int main()
{
	scanf("%d%d",&n,&m);
	pw[0]=1;
	for(int i=1;i<=m;++i)
	{
		int u,v;scanf("%d%d",&u,&v);
		--u,--v;
		G[(1<<u)+(1<<v)]=1;
		pw[i]=2*pw[i-1]%mod;
	}
	for(int i=1;i<(1<<n);++i)
	{
		for(int j=0;(1<<j)<=i;++j)
			if(i&(1<<j))
				for(int k=0;k<j;++k)
					if(i&(1<<k))
						cnt[i]+=G[(1<<j)+(1<<k)];
	}
	f1[1]=1;
	for(int i=2;i<(1<<n);++i)
	{
		f1[i]=pw[cnt[i]];
		for(int j=(i-1)&i;j;j=(j-1)&i)
			if(j&1)f1[i]=(f1[i]-1ll*f1[j]*pw[cnt[i-j]]%mod+mod)%mod;
	}
	f2[2]=1;
	for(int i=3;i<(1<<n);++i)
	{
		f2[i]=pw[cnt[i]];
		for(int j=(i-1)&i;j;j=(j-1)&i)
			if(j&2)f2[i]=(f2[i]-1ll*f2[j]*pw[cnt[i-j]]%mod+mod)%mod;
	}
	int ans=pw[m],T=(1<<n)-1;
	for(int i=1;i<=T;++i)
	{
		if(!(i&1))continue;
		int S=T-i;
		for(int j=S;j;j=(j-1)&S)
			if(j&2)
			{
				if(cnt[i+j]>cnt[i]+cnt[j])continue;
				ans=(ans-1ll*f1[i]*f2[j]%mod*pw[cnt[T-i-j]]%mod+mod)%mod;
			}
	}
	cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/zmyzmy/p/13674429.html