LOJ#2127. 「HAOI2015」按位或

题目描述:戳这里
题解:

这题如果按照题意做看似非常不可解,但是有一个叫做Min-Max容斥的东西:
M a x ( S ) = ∑ U ⊂ S ( − 1 ) ∣ U ∣ − 1 M i n ( U ) Max(S)=\sum_{U\subset S}(-1)^{\left| U \right|-1}Min(U) Max(S)=US(1)U1Min(U)
对于这题,Max就是答案,也就是 ∣ | 2 n − 1 2^n-1 2n1的期望步数。
Min就是对于当前的二进制位的集合,至少 ∣ | 到一位是1的期望步数。
那么接下来的问题就变成了求Min。
我们列出式子:
M i n ( U ) = 1 ∑ T ∩ U ≠ ∅ P T Min(U)=\frac{1}{\sum_{T \cap U \ne \emptyset}P_T} Min(U)=TU̸=PT1
补集转换:
= 1 1 − ∑ T ∩ U = ∅ P T =\frac{1}{1-\sum_{T \cap U = \emptyset}P_T} =1TU=PT1
对U再取个补集:
= 1 1 − ∑ ( T ∪ C R U ) = T P T =\frac{1}{1-\sum_{(T \cup C_RU)= T}P_T} =1(TCRU)=TPT1
那么就是求 C R U C_RU CRU的子集的P之和。
这可以直接用一个 2 n ⋅ n 2^n \cdot n 2nn的DP就可以预处理出来。
所以总的复杂度就是 O ( 2 n ⋅ n ) O(2^n \cdot n) O(2nn)
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=25,maxm=(1<<20)+5;
int n;
double ans,f[maxm];
double abs_(double x){if (x<0.0) return -x; return x;}
int main(){
	scanf("%d",&n);
	int now=0;
	for (int i=0;i<(1<<n);i++) {
		scanf("%lf",&f[i]);
		if (abs_(f[i])>1e-7) 
			now=now|i;
	}
	if (now^((1<<n)-1)) {
		printf("INF\n");
		return 0;
	}
	for (int i=1;i<=n;i++)
	for (int j=0;j<(1<<n);j++)
		if (j&(1<<i-1)) f[j]+=f[j^(1<<i-1)];
	for (int i=1;i<(1<<n);i++){
		int sum=0;
		for (int j=1;j<=n;j++) if (i&(1<<j-1)) sum++;
		int x=i^((1<<n)-1);
		if ((sum-1)%2==0) ans+=1.0/(1.0-f[x]); else ans-=1.0/(1.0-f[x]);
	}
	printf("%.10lf\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值