bzoj4036 [HAOI2015]按位或 FWT+min-max容斥

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/87923436

Description


刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal
的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1问期望多少秒后,你手上的数字变成2^n-1。
n 20 n\le20

Solution


考虑min-max容斥,我们枚举一次就选中的位记为T,问题在于求E(T)
不妨记 P ( T ) = R T p ( R ) P(T)=\sum\limits_{R|T\neq\empty}{p(R)} ,显然 E ( T ) = 1 P ( T ) E(T)=\frac{1}{P(T)}
考虑补集转化,记 U = ( T ) , U=\complement(T), 那么 P ( T ) = 1 S U p ( S ) P(T)=1-\sum\limits_{S\subset U}{p(S)}
这里求 T S p ( T ) \sum\limits_{T\subset S}p(T) 也就是子集和,实际上直接做一次FWT就可以了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>

const int N=1049576;

double p[N];

int c[N];

void FWT(double *a,int n) {
	for (int i=1;i<n;i<<=1) {
		for (int j=0;j<n;j+=(i<<1)) {
			for (int k=0;k<i;++k) {
				a[j+k+i]+=a[j+k];
			}
		}
	}
}

int main(void) {
	freopen("data.in","r",stdin);
	int n; scanf("%d",&n);
	int lim=1<<n;
	for (int i=0;i<lim;++i) {
		scanf("%lf",&p[i]);
		c[i]=c[i>>1]^(i&1);
	}
	FWT(p,lim);
	double ans=0;
	for (int i=1;i<lim;++i) {
		if ((1-p[(lim-1)^i])>1e-8) {
			ans+=(c[i]?(1.0):(-1.0))/(1-p[(lim-1)^i]);
		}
	}
	if (ans<1e-8) puts("INF");
	else printf("%.10lf\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/87923436