题目链接
minmax容斥又称最值反演,是一种针对集合min->max或者max->min的反演
结论公式为
m a x { S } = ∑ T ⊆ S ( − 1 ) ∣ T ∣ + 1 m i n { T } max\{S\}=\sum_{T\subseteq S}(-1)^{|T|+1}min\{T\} max{S}=∑T⊆S(−1)∣T∣+1min{T}
m i n { S } = ∑ T ⊆ S ( − 1 ) ∣ T ∣ + 1 m a x { T } min\{S\}=\sum_{T\subseteq S}(-1)^{|T|+1}max\{T\} min{S}=∑T⊆S(−1)∣T∣+1max{T}
这个应该还蛮好证明的
我们考虑构造一个函数
m a x { S } = ∑ T ⊆ S F ( ∣ T ∣ ) m i n { T } max\{S\}=\sum_{T\subseteq S}F(|T|)min\{T\} max{S}=∑T⊆SF(∣T∣)min{T}
那么分析一下第 x + 1 x+1 x+1大的数字会被枚举到几次
显然是从比他大的 x x x个里任意取出若干个与他构成集合,也就是 ∑ i = 0 x ( x i ) \sum_{i=0}^{x}{x\choose i} ∑i=0x(ix)个
然后他出现的次数应该是 ∑ i = 0 x ( x i ) f ( i + 1 ) \sum_{i=0}^{x}{x\choose i}f(i+1) ∑i=0x(ix)f(i+1)
显然只有第0大(最大)的才会出现次数是一,所以我们规定大函数 F ( x ) = [ x = = 0 ] F(x)=[x==0] F(x)=[x==0]
F ( x ) = ∑ i = 0 x ( x i ) f ( i + 1 ) F(x)=\sum_{i=0}^{x}{x\choose i}f(i+1) F(x)=∑i=0x(ix)f(i+1)
愉快的二项式反演一波
f ( x + 1 ) = ∑ i = 0 x ( − 1 ) x − i ( x i ) F ( i ) f(x+1)=\sum_{i=0}^{x}(-1)^{x-i}{x\choose i}F(i) f(x+1)=∑i=0x(−1)x−i(ix)F(i)
f ( x + 1 ) = ∑ i = 0 x ( − 1 ) x − i ( x i ) [ i = = 0 ] f(x+1)=\sum_{i=0}^{x}(-1)^{x-i}{x\choose i}[i==0] f(x+1)=∑i=0x(−1)x−i(ix)[i==0]
f ( x + 1 ) = ( − 1 ) x f(x+1)=(-1)^{x} f(x+1)=(−1)x
f ( x ) = ( − 1 ) x − 1 f(x)=(-1)^{x-1} f(x)=(−1)x−1
但其实 ( − 1 ) x − 1 (-1)^{x-1} (−1)x−1与 ( − 1 ) x + 1 (-1)^{x+1} (−1)x+1也没啥差别对吧,所以最值反演按照上面表述也是正确的。
同样的minmax容斥可以被扩展成这样子两个式子
l c m { S } = ∏ T ⊆ S g c d { T } lcm\{S\}=\prod_{T \subseteq S}gcd\{T\} lcm{S}=∏T⊆Sgcd{T}
E [ m a x { S } ] = ∑ T ⊆ S ( − 1 ) ∣ T ∣ + 1 E [ m i n { T } ] E[max\{S\}]=\sum_{T\subseteq S}(-1)^{|T|+1}E[min\{T\}] E[max{S}]=∑T⊆S(−1)∣T∣+1E[min{T}]
这道题要用到的就是后者。
我们转化题意就是求全集中最后一个1出现的期望
用minmax容斥可以转化成集合中第一个1出现的期望
这个期望就是概率分之一,所以直接考虑概率
显然第一个1出现的概率就是1减去这个集合的补集的所有子集出现的概率和
子集和什么的FMT就可以达到正确复杂度了
那么这题就做完了
代码如下:
#include<bits/stdc++.h>
using namespace std;
int lim,n;
double p[1100000],ans;
int cnt[1100000];
int main()
{
scanf("%d",&n);
lim=1<<n;
for(int i=0;i<lim;i++) scanf("%lf",&p[i]);
for(int mid=1;mid<lim;mid<<=1)
{
for(int i=0;i<lim;i+=(mid<<1))
{
for(int j=0;j<mid;j++)
{
p[i+j+mid]+=p[i+j];
}
}
}
for(int i=1;i<lim;i++)
{
cnt[i]=cnt[i>>1]+(i&1);
}
for(int i=1;i<lim;i++)
{
if(1.0-p[(lim-1)^i]<1e-9)
{
puts("INF");
return 0;
}
ans+=((cnt[i]&1)?1.0:-1.0)*(1.0/(1.0-p[(lim-1)^i]));
}
printf("%.6lf",ans);
}