洛谷P3175 [HAOI2015]按位或 (min-max容斥 + 高维前缀和)

题目链接:https://www.luogu.com.cn/problem/P3175

\(min-max\) 容斥公式 : $$max(S) = \sum\limits_{T!=\phi, T\subseteq S}(-1)^{T-1}min(T)$$

\[min(S) = \sum\limits_{T!=\phi, T\subseteq S}(-1)^{T-1}max(T) \]

公式在期望意义下也成立,当我们无法比较集合中元素的大小关系时,可以通过 \(min-max\) 容斥求出最大值或最小值

将每个位置看成一个元素,设 \(max(S)\) 表示集合中最后一个元素被或为 \(1\) 的时间,最后一个元素被或为 \(1\),也即所有元素都被或为 \(1\)\(min(T)\) 表示集合中第一个元素被或为 \(1\) 的时间,根据定义,由无穷级数求和,概率为 $$\frac{1}{\sum\limits_{X\cap T != \phi}p(X)}$$

简单容斥一下,变为 $$\frac{1}{1-\sum\limits_{X\cap T = \phi}p(X)}$$

后者可以通过计算集合 \(T\) 的子集和求出,高维前缀和即可(fwt也可以)

注意判断无解的情况,当分子趋向于 \(0\),即永远有元素无法被或为 \(1\) 时无解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 200010;
const int maxm = (1 << 20) + 10;
const double eps = 1e-8;

int n, m, N; ll k;
double p[maxm];
char s[maxn];

int count(int x){
	int cnt = 0;
	while(x){
		if(x&1) ++cnt;
		x >>= 1;
	}
	return cnt;
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	scanf("%d", &n);
	for(int i = 0 ; i < (1 << n) ; ++i) scanf("%lf", &p[i]);
	
	for(int j = 0 ; j < n ; ++j){
		for(int i = 0 ; i < (1 << n) ; ++i){
			if((i>>j)&1) p[i] = p[i] + p[i ^ (1<<j)];
		}
	}
	
	int all = (1 << n) - 1;
	
	double ans = 0;
	for(int i = 1 ; i < (1 << n) ; ++i){
		int cnt = count(i);
		if(fabs(1.0-p[all^i] < eps)) {
			printf("INF\n"); return 0;
		}
		int f = (cnt & 1) ? 1 : -1;
		ans += 1.0 * f * (1.0 / (1.0-p[all^i]));
	}
	printf("%.6lf\n", ans);
	
	return 0;
}
posted @ 2021-10-01 21:01  Tartarus_li  阅读(49)  评论(0编辑  收藏  举报