[HAOI2015]按位或

朴素的

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

 

posted @ 2019-01-05 17:53  *Miracle*  阅读(335)  评论(0编辑  收藏  举报