期望题选做

P 4316 一张无向图之上求从1到n经过路径总长度的期望。看起来 很不可做 但是由于 线性运算的期望==期望的线性运算 这道题变得可做起来了。

别问我这个等式 我很迷的利用其解决一些问题,我还没入门 服了。我们倒推 我也不知道为什么要倒推但是显然的是 终点是一切期望的开始。

面对这些题目 我是该沉思我什么时候能秒题呢?什么时候才能 全部都理解呢,我期望 那一天 很快到来吧。

论 期望: 我见到的题目大致按照我的感觉可以分为两种 一种是条件期望 一种是排列期望。

对于条件期望,其实就是 在满足 任意可能性的情况之下 达到的状态 这个状态也以另一种可能性达到另一个状态。

显然有我们把所有可能的结果都生成 用其结果*其概率就是我们的均值期望了。不过这样显然是不可能的当事件过多我们不可能把所有的情况列出来从而得到结果。

但是期望有一个 最伟大的性质是 线性可加的。有限个随机变量之和的数学期望==每个随机变量的数学期望之和。

为什么 ?

看起来如此的显然 在我眼里相当的 不可思议诶。下一刻 怎么这么显然我都看不出来。

仔细思考 发现的确很显然吧 在某种角度来看。接下来做题 从我的那个显然的角度出发。

有向无环图 考虑f[x] 表示从x 到 n的期望跑过的路径长度。那么 递推式子就很好写了。

注明一个 容易wa 的点 : 关于精度的问题当让保留几位小数的时候printf 输出限定几位小数是会四舍五入的。这里容易出错一旦四舍五入的话,造成精度问题。

这类问题在实数二分的时候会经常出现 一般的比较准确的是得到答案ans 后 将ans *10^x 赋给一个整形的变量 然后 再赋给一个double型的 /10^x 然后就好了 没有什么四舍五入 这点要注意。

或者就是直接 整数二分 全部扩大 最后再除 保证不会出现四舍五入的情况出现。这里注意 有些题就是让四舍五入。

中午颓博客+睡觉好像是惯例了吧。。毕竟 人心是需要温暖的。

头脑清醒的时候效率果然高!

P1297 这就是上述式子的应用 我们发现 枚举出所有的情况是不可能的 既然线性可加 那么我们自己求自己的期望 累加在一起就是总体的。

对于某一题 它作对的期望之和它上一题有关 总情况显然是 a[i]*a[i-1] 作对的可能情况有min(a[i],a[i-1])种那么我们求每一道题的期望 并将其累加就得到了答案。

//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 1000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
#define up(p,i,w) for(ll i=p;i<=w;++i)
using namespace std;
const ll MAXN=10000000;
ll n,A,B,C;
ll a[MAXN];
db ans;
int main()
{
    freopen("1.in","r",stdin);
    scanf("%d%d%d%d%d",&n,&A,&B,&C,a+1);
    for (ll i=2;i<=n;i++)
    a[i]=((ll)a[i-1]*A+B)%100000001;
    for(ll i=1;i<=n;i++)
    a[i]=a[i]%C+1;
    //for(ll i=1;i<=n;++i)cout<<a[i]<<endl;
    ans+=(db)(min(a[n],a[1]))/((db)a[1]*a[n]);
    for(ll i=2;i<=n;++i)ans+=(db)(min(a[i],a[i-1]))/((db)a[i]*a[i-1]);
    printf("%.3lf",ans);
    return 0;
}
View Code

这题就比较有意思 问期望次数 这几种能量晶体 每次随机选出一个 也就是生成一个排列 一个一个用 问这个排列种 有多少次 1-7这连续在一起。且这个排列也是随机生成的。

首先考虑有多少种排列 n!->n!/a1!a2!a3!a4!a5!a6!a7!这玩意好像是的...很容易看出来。

考虑在这么多种排列之中有多少情况是合法的。显然一个排列想要合法情况的数量上界为 设ans=min(a1,a2,a3,a4,a5,a6,a7);

也就是说其中的必然有一个排列的自身的价值为ans 想办法统计答案就好了。似乎不太能写,我们固定价值 找 排列数量 非常难找。除了极端情况。

那么此时考虑 生成一个合法序列的概率有多大?此时也是期望 以为生成的话价值为1 乘起来就是了。

不考虑排列此时 那么我们选取第一个的概率为 a1/n 第二个的概率为 a2/n-1......这样 考虑排列呢 其实也没有什么关系 因为 这样每种排列的成功概率都一样。

乘上排列数量就好了吧 那么这是价值为1的期望 价值为2 呢 我们此时因为是线性可加的而那些失败的不带来价值不进行累加。

显然有状态 f[i] 表示 前i个魔法形成的期望价值是多少:显然的是1-7 是7!*a1*...a7/n*(n-1)*...(n-6);

后面 每一项 的概率都是这个玩意 , 很崩溃对么 我 也很...没有往后面的期望和前面的一样这个方面来思考。

证明:譬如第8项我们随便给他找一个数字 因为我们前7项是一个排列 a1吧 此时概率就是前面那堆东西 a1-1/n-7此时才会有价值的出现。

然后 我们发现对于2-8这个东西排列也是7!且 前面第一个数字可能性有7种 我们把这七种可能加起来发现后面这个sigama ax-1/n-7==1

然后 乘上上述价值刚好是第一个排列的期望。于是答案就是 7!*第一个排列的期望*n-6。

//#include<bits/stdc++.h>
#include<iostream>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<utility>
#include<vector>
#include<iomanip>
#include<cstdlib>
#define INF 1000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define RE register
#define EPS 1e-8
#define ll long long
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=10;
db ans=1;
ll a[MAXN],n,flag=10;
int main()
{
    //freopen("1.in","r",stdin);
    for(ll i=1;i<=7;++i)a[i]=read(),n+=a[i],flag=min(flag,a[i]);
    if(!flag){puts("0.000");return 0;}
    for(int i=1;i<=7;++i)ans*=(db)((db)a[i]/(n-i+1));
    for(ll i=1;i<=7;++i)ans=ans*i;
    ans=ans*(n-6);
    printf("%.3lf",ans);
    return 0;
}
View Code

LINK:小猪佩奇玩游戏 期望基础题目 但是我好像不会写来着当时一眼也没看出来正解所以弃疗了。

思路还是比较容易得到的 考虑每个数字都要消除 一次的话不免有其他的数字会给其带来影响 所以一个数字消除的概率是 1/(s+1) 其中s 是一些数字经过k次幂后得到的个数。

发现n只有1e9 一些数字是没有平方根或者立方根的 所以考虑枚举平方根立方根...生产对应的数字 那么这样的范围也就是sqrt(n)了。

生成的数字最多 每次log个 hash存起来最后便利一下邻接表即可。思路还是比较清晰的(map好像会T的样子。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 100000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define EPS 1e-5
#define P 19260817ll
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int mod=1000003;
int T;
int n,m,len;
db ans=0;
int lin[mod],nex[mod],e[mod];
int ver[mod];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline int calc(int x,int y,int z)
{
    for(int i=lin[x];i;i=nex[i])
    {
        if(ver[i]==y)
        {
            e[i]+=z;
            return e[i];
        }
    }
    return 0;
}
inline int find(int x)
{
    int s=x*P%mod;
    for(int i=lin[s];i;i=nex[i])
    {
        if(ver[i]==x)return e[i];
    }
    return 0;
}
inline void insert(int w,int y)
{
    int s=w*P%mod;
    if(!calc(s,w,y))add(s,w,y);
}
int main()
{
    freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        memset(lin,0,sizeof(lin));
        len=0;n=read();ans=0;
        m=(int)(sqrt(1.0*n));
        //cout<<m<<endl;
        for(int i=2;i<=m;++i)
        {
            ll w=i*i;
            while(w<=n)
            {
                insert(w,1);
                w=w*i;
            }
        }
        for(int i=1;i<=len;++i)
        {
            int w=find(ver[i]);
            ans=ans+1.0/((w+1)*1.0);
        }
        ans=ans+(n-len);
        printf("%.5lf
",ans);
    }
    return 0;
}
View Code

 LINK:时间跳跃 这道期望的题目我做的一塌糊涂 什么都不是。写在前面 我应该尽全力去思考。

题目大意就是 随便选一些边构成的凸多边形的期望值。一个比较显然的想法是爆搜 可以得到20分。

至于判定条件 如何构成凸多边形 显然的是 类比于三角形 最长边不能大于其他边之和 。

我们容易想到设状态 f[i] 表示 最长边和其他边差值为j的期望 这个东西难以转移下去不知道方案数大致是这样的。

考虑实质上还是 若干边和其他边的组合 我们考虑枚举一个最长边 那么其能带来的贡献 其他小于它的边构成的比其长的集合 带来的贡献了。

当前边是 i 那么就是前i-1条边所构成的边的长度要大于i 这里有一个 不需要考虑的细节围成多边形显然的是 至少3条边但是除了最长的边 剩下的边再想大于当前边至少需要2条。

于是有了这样的一个算法 即 f[i][j] 表示前i条边构成长度为j的方案数,好像少点什么 代价 加上去吧 f[i][j][k] 其中k表示此时用了多少条边。

转移还是很显然的时间n^3 且对空间消耗较大。(其实可以考虑打表优化了但好像也不行的样子因为j需要枚举的范围实在是太大了。

考虑不合法的 只有[1,i]这个范围我们求出这个范围中的东西 全集减一下即可。全集是什么 对于某个i来说考虑选了j条边乘以代价即全集最后别忘了要成概率。

空间复杂度 经过滚动数组就可以优化到n^2了 打表 即可。

考虑如何把时间优化到n^2 考虑再设一个数组g[i][j]表示 前i个数构成j的边数的代价 f[i][j] 表示前i个数构成j的方案数。

f数组的转移显然是01背包。g数组?g[i][j]=g[i-1][j]+g[i-1][j-i]+f[i-1][j-i];这样我们就成功把时间复杂度降到了n^2.

 //#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define mod 1000000007
#define ull unsigned long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ll MAXN=5010;
ll maxx,T;
ll f[2][MAXN],fac[MAXN];//f[i][j]表示前i个数构成j的方案数
ll g[2][MAXN];//g[i][j]表示前i个数构成j的权值和
ll w[MAXN],ans[MAXN],b[MAXN],c[MAXN],inv[MAXN];
ll v[MAXN],s[MAXN];
//w[i]表示前i条边的答案 c[i] 表示第i条边的全集和
inline ll fast_pow(ll b,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)ans=ans*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return ans;
}
inline void prepare()
{
    fac[0]=1;
    for(ll i=1;i<=maxx;++i)fac[i]=fac[i-1]*i%mod;
    inv[maxx]=fast_pow(fac[maxx],mod-2);
    for(ll i=maxx-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(ll a,ll b){return fac[a]*inv[b]%mod*inv[a-b]%mod;}
signed main()
{
    //freopen("1.in","r",stdin);
    T=read();
    for(ll i=1;i<=T;++i)
    {
        ans[i]=read();
        maxx=max(maxx,ans[i]);
    }
    ll u=0;
    f[u][0]=1;
    prepare();
    for(ll i=1;i<=maxx;++i)
    {    
        for(ll j=0;j<=i;++j)
        {
            w[i]=(w[i]+g[u][j])%mod;
            s[i]=(s[i]+f[u][j])%mod;
        }
        u=u^1;
        for(ll j=maxx;j>=0;--j)
        {
            f[u][j]=f[u^1][j];
            if(j>=i)f[u][j]=(f[u][j]+f[u^1][j-i])%mod;
            g[u][j]=g[u^1][j];
            if(j>=i)g[u][j]=(g[u][j]+g[u^1][j-i]+f[u^1][j-i])%mod;
        }
    }
    //考虑求出答案 全集减补集
    b[0]=1;
    for(ll i=1;i<=maxx;++i)
    {
        //对于第i条边作为最长边 补集的代价为w[i]+s[i]都是不和法的。
        //对于强制选择第i条边的全集为 C(i-1,j)*j+C(i-1,j);
        v[i]=v[i-1];
        b[i]=b[i-1]*inv[2]%mod;
        ll sum=0;
        for(ll j=0;j<=i-1;++j)
            sum=(sum+C(i-1,j)*(j+1))%mod;
        //cout<<i<<' '<<sum<<' '<<sum-w[i]-s[i]<<endl;
        v[i]=((v[i]+sum-w[i]-s[i])%mod+mod)%mod;
    }
    for(ll i=1;i<=T;++i)
    {
        ll x=ans[i];//第x条边的答案
        printf("%lld
",v[ans[x]]*b[ans[x]]%mod);
    }
    return 0;
}
View Code

!惊了好题qwq。

好久没更期望题了 概率与期望 我咋啥都不会啊。

原文地址:https://www.cnblogs.com/chdy/p/11378298.html