【BZOJ4036】[HAOI2015] 按位或(Min-Max容斥+FWT)

点此看题面

大致题意: 每个单位时间有(p_i)的概率选中(i∈[0,2^n-1]),求期望多少时间后选中的数按位或的结果变成了(2^n-1)

前言

典型的数学题:看起来很吓人的知识点+似乎很高深的分析+短短几行的代码。。。

分析

要做这道题,首先我们要知道一个叫做(Min-Max)容斥的东西:

[E(max(S))=sum_{Tsubseteq S}(-1)^{|T|+1}E(min(T)) ]

具体地,在这题中,(E(min(T)))表示(T)中出现任意一位的期望时间,(E(max(S)))表示(S)中所有位都出现的期望时间(这里的(S)即为(2^n-1))。

换言之,我们只要能求出(E(min(T))),就可以在(O(2^n))的时间复杂度内通过容斥求出(E(max(S)))。那么我们只需要考虑如何求一个集合中出现任意一位的期望时间。

首先自然有一个很暴力的式子:

[E(min(T))=frac1{sum_{i∩T ot=varnothing}p_i} ]

这个式子的关键是分母中的概率,且这个东西显然不好维护。因此我们反过来考虑:出现任意一位的概率就是(1-)不出现任意一位的概率。

也即是说:

[E(min(T))=frac1{1-sum_{i∩T=varnothing}p_i} ]

(k=T xor (2^n-1))(即(T)的补集),那么这个式子就相当于是:

[E(min(T))=frac1{1-sum_{isubseteq k}p_i} ]

显然(sum_{isubseteq k}p_i)可以用(FWT)高效地求出,于是这题就做完了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20
#define DB double
using namespace std;
int n,s,op[1<<N];DB p[1<<N];
int main()
{
	RI i,g=0;for(scanf("%d",&n),s=1<<n,i=0;i^s;++i) scanf("%lf",p+i);//读入
	for(i=0;i^s;++i) p[i]&&(g|=i);if(g^(s-1)) return puts("INF"),0;//判无解
	RI j,k;for(i=1;i^s;i<<=1) for(j=0;j^s;j+=i<<1) for(k=0;k^i;++k) p[i+j+k]+=p[j+k];//FWT
	DB t=0;for(op[0]=-1,i=0;i^s;++i)//Min-Max容斥
		op[i]=op[i>>1]*(i&1?-1:1),p[i^(s-1)]<1-1e-8&&(t+=op[i]/(1-p[i^(s-1)]));
	return printf("%.10lf
",t),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4036.html