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

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

(min-max) 容斥公式 : $$max(S) = sumlimits_{T!=phi, Tsubseteq S}(-1)^{T-1}min(T)$$

[min(S) = sumlimits_{T!=phi, Tsubseteq S}(-1)^{T-1}max(T) ]

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

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

简单容斥一下,变为 $$frac{1}{1-sumlimits_{Xcap 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
"); return 0;
		}
		int f = (cnt & 1) ? 1 : -1;
		ans += 1.0 * f * (1.0 / (1.0-p[all^i]));
	}
	printf("%.6lf
", ans);
	
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/15360091.html