朴素的
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,就合法了。