朴素的
f[S]表示S到(1<<n)的期望次数
发现1的个数只增加不减少
所以可以类似拓扑序的图,然后枚举子集O(3^n)转移
没有优化的余地
另辟蹊径:
拆开每一位来看
t[i]表示第i位变成1的次数
ans=E(max(t[i]))
根据min-max容斥
得到:ans=∑E(t[i])-∑E(min(t[i],t[j]))+∑E(min(t[i],t[j],t[k])).....
考虑min(S)的含义:
使得这个S中的任意一位0变成1的期望次数!
操作一次变成1的概率tmp:(1-p(操作一次不包含S))
(这个用FMT)
(当然,可以直接计算操作一次包含S的概率,但是直接FMT会把0算上,,这个要特殊处理)
1/tmp就是期望次数
根据S的size的奇偶确定符号
代码:
#include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=22; double p[1<<20]; int sz[1<<20]; double ans; int n; int main(){ rd(n); for(reg i=0;i<(1<<n);++i){ scanf("%lf",&p[i]); sz[i]=sz[i>>1]+(i&1); } for(reg i=0;i<n;++i){ for(reg j=0;j<(1<<n);++j){ // cout<<" i j "<<i<<" "<<j<<endl; if(j&(1<<i)) p[j]+=p[j^(1<<i)]; } } // cout<<" FMT "<<endl; int s=(1<<n)-1; for(reg i=1;i<(1<<n);++i){ int bu=s-i; double tmp; // cout<<" bu "<<bu<<" p[bu] "<<p[bu]<<endl; if(p[bu]!=1.000) tmp=1/(1.0-p[bu]); else { puts("INF"); return 0; } if(sz[i]&1){ ans+=tmp; }else ans-=tmp; } printf("%.10lf",ans); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/1/4 22:15:10 */
如果想到min-max容斥和式子
应该就比较好做了。
正难则反的原因是:
max要考虑最后一个变成1的次数,都要考虑上,太麻烦
min只要考虑第一个变成1的次数,有一个操作涉及S,就合法了。