LOJ#2127. 「HAOI2015」按位或

版权声明:原创文章,转载要注明作者哦 https://blog.csdn.net/DYT_B/article/details/85297532

题目描述:戳这里

题解:

这题如果按照题意做看似非常不可解,但是有一个叫做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就是答案,也就是 | 2 n 1 2^n-1 的期望步数。
Min就是对于当前的二进制位的集合,至少 | 到一位是1的期望步数。
那么接下来的问题就变成了求Min。
我们列出式子:
M i n ( U ) = 1 T U P T Min(U)=\frac{1}{\sum_{T \cap U \ne \emptyset}P_T}
补集转换:
= 1 1 T U = P T =\frac{1}{1-\sum_{T \cap U = \emptyset}P_T}
对U再取个补集:
= 1 1 ( T C R U ) = T P T =\frac{1}{1-\sum_{(T \cup C_RU)= T}P_T}
那么就是求 C R U C_RU 的子集的P之和。
这可以直接用一个 2 n n 2^n \cdot n 的DP就可以预处理出来。
所以总的复杂度就是 O ( 2 n n ) O(2^n \cdot n)
代码如下:

#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;
}

猜你喜欢

转载自blog.csdn.net/DYT_B/article/details/85297532