[HAOI2015][loj2127]按位或

14 篇文章 0 订阅
9 篇文章 0 订阅

1 前言

大概又是Min-Max容斥与FMT的裸题吧
刚学完Min-Max容斥,式子推的飞快

2 题目相关

2.1 题目大意

一开始你手上有一个数 0 0 0,每次或上随机一个在 [ 0 , 2 n ) [0,2^n) [0,2n)中的数(给出随到每个数的概率),求期望多少步你手上的数变为 2 n − 1 2^n-1 2n1

2.2 数据范围

n ≤ 20 n\le 20 n20,所有概率的输入以及期望步数的输出都使用实数

3 题解

我们发现我们可以直接Min-Max容斥
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}=TS(1)T+1min{T}
我们发现 m a x { S } max\{S\} max{S}相当于是变为 2 n − 1 2^n-1 2n1的期望步数, m i n { T } min\{T\} min{T}代表进行操作与集合 T T T有交的期望步数
我们就可以直接做了
我们知道,如果求出了 m i n { T } min\{T\} min{T}我们可以直接 Θ ( 2 n ) \Theta(2^n) Θ(2n)计算答案了
考虑如何计算 m i n { T } min\{T\} min{T}
设取到 S S S的概率为 g S g_S gS,我们发现 m i n { S } = 1 1 − ∑ T ∩ S = ∅ g T min\{S\}=\frac{1}{1-\sum_{T\cap S=\varnothing}g_T} min{S}=1TS=gT1
我们相当于要求 f S = ∑ T ∩ S = ∅ g T f S = ∑ T ∩ ( C u S ) = T g T f S = ∑ T ⊆ ( C u S ) g T \begin{aligned} f_{S}&=\sum_{T\cap S=\varnothing}g_T\\ f_{S}&=\sum_{T\cap(Cu_S)=T}g_T\\ f_{S}&=\sum_{T\subseteq(Cu_S)}g_T\\ \end{aligned} fSfSfS=TS=gT=T(CuS)=TgT=T(CuS)gT
然后翻转一下,就是子集卷积了,使用FMT即可
非常方便

4 代码

贴上我写的第一份代码(未卡常版本)

#include<cstdio>
#include<cctype>
#include<algorithm>
namespace fast_IO
{
    const int IN_LEN=10000000,OUT_LEN=10000000;
    char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
    inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
    inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
    inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
    char cu=getchar();x=0;bool fla=0;
    while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
    while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
    if(fla)x=-x;
}
double xs[11][10];
template <typename T> inline void Read(T&x)
{
    char cu=getchar();x=0;bool fla=0;
    while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
    while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
    if(cu=='.')
    {
        cu=getchar();int t=0;
        while(isdigit(cu))x+=xs[++t][cu-'0'],cu=getchar();
    }
    if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
    if(x>=10)printe(x/10);
    putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
    if(x<0)putchar('-'),printe(-x);
    else printe(x);
}
int bit[21],n,lenth;
double g[2097152],ans;
bool pc[2097152];
int main()
{
    xs[0][1]=1;
    for(rg int i=1;i<=10;i++)
    {
        xs[i][1]=xs[i-1][1]/10;
        for(rg int j=1;j<=9;j++)xs[i][j]=xs[i][1]*j;
    }
    bit[0]=1;
    for(rg int i=1;i<=20;i++)bit[i]=bit[i-1]<<1;
    read(n),lenth=bit[n];
    for(rg int i=lenth-1;i>=0;i--)Read(g[i]);
    for(rg int i=1;i<lenth;i<<=1)
        for(rg int j=0;j<lenth;j+=(i<<1))
            for(rg int k=0;k<i;k++)
                g[j+k]+=g[j+k+i];
    const int Low=lenth-1;
    for(rg int i=1;i<lenth;i++)
    {
        pc[i]=pc[i>>1]^(i&1);
        if(g[i]>=0.999999999)
        {
            puts("INF");
            return flush(),0;
        }
        if(pc[i])ans+=1.0/(1-g[i]);
        else ans-=1.0/(1-g[i]);
    }
    printf("%.8lf",ans);
    return flush(),0;
}

5 总结

这道题我进行了一波卡常(你看我写的第一份代码已经用实数读优了
然后我学习到了一个小技巧
这题里的FMT我用的是FWTand的写法(写FWTor也可以,反正不是两重循环的)
然后对于FWT,将最底下的两层单独提出来用for循环做,速度会有明显的提升
就不贴卡常后的代码了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值