BZOJ5006 [THUWC2017]Bipartite 随机二分图

传送门

题解:
考试时忙着想前面两道题了,没怎么看。
看这个n的范围,估计是状压。
考虑 d p [ i ] [ j ] dp[i][j] dp[i][j],左边匹配状态为i,右边为j时的概率。显然两边匹配点数相同。答案是 d p [ 2 n − 1 ] [ 2 n − 1 ] × 2 n dp[2^n-1][2^n-1]\times{2^n} dp[2n1][2n1]×2n
当然不能直接开数组。map吼哇。
如果t=0,直接转移就行了。
如果t=1,把边拆掉,各自连接两条边,期望 1 2 \frac{1}{2} 21,再把四个点都连接起来,期望 1 4 \frac{1}{4} 41
如果t=2,和t=1基本相同,把四个点连接起来时期望是 − 1 4 -\frac{1}{4} 41

因为同时出现的边组(x1,y1,x2,y2),在x1或y1已经被匹配的时候能对匹配(x2,y2)产生50%的贡献,在x2或y2已经被匹配的时候能对匹配(x1,y1)产生50%的贡献,但是对同时匹配(x1,y1),(x2,y2)只能产生25%的贡献,所以要补上25%的同时出现的贡献。
同理只出现一条的边组(x1,y1,x2,y2),在拆成(x1,y1)(x2,y2)时,对同时匹配(x1,y1)(x2,y2)时会多产生25%的贡献,所以要加上一组-25%同时出现的边组
by:LowestJN

至于连四个点时要特判的原因…我猜是因为如果有相交,因为一条边只会出现一次,所以两边的点就会不一样,就会导致一个不合法的结果。但是分别连的时候就不影响答案?emmm…大概是匹配到一条边之后就不会匹配到与之相交的另一条边?

这题面真是有毒QAQ出题人想表达得到了图之后匹配的个数?

代码:

#include<cstdio>
#include<map>
#define maxn 32770
#define maxm 1005
#define mod 1000000007
#define inv2 500000004
#define inv4 250000002
using namespace std;
int n,m,st[maxm],p[maxm],cnt;
map<int,int> dp[maxn];
int DP(int S)
{
	if(!S) return 1;
	int s=S&((1<<n)-1),t=S>>n;
	if(dp[s].count(t)) return dp[s][t];
	int res=0;
	for(int i=1;i<=cnt;i++)
		if((S&st[i])==st[i]&&S<st[i]*2) res=(res+1ll*DP(S^st[i])*p[i]%mod)%mod;
	return dp[s][t]=res;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int tp,a1,b1,a2,b2,x,y;
		scanf("%d%d%d",&tp,&a1,&b1); a1--,b1+=n-1;
		cnt++,x=st[cnt]=(1<<a1)|(1<<b1),p[cnt]=inv2;
		if(tp)
		{
			scanf("%d%d",&a2,&b2); a2--,b2+=n-1;
			cnt++,y=st[cnt]=(1<<a2)|(1<<b2),p[cnt]=inv2;
			if(!(x&y)) cnt++,st[cnt]=x|y,p[cnt]=tp==1?inv4:(mod-inv4);
		}
	}
	printf("%lld\n",(1ll<<n)*DP((1<<(n*2))-1)%mod);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值