【概率和期望】有趣的随机游走问题

没有几天了呀,明天义冢的彭友们要到我们学校来虐我,瑟瑟发抖。概率和期望这块一直都觉得很难理解,感觉期望和概率还是十分重要的模块,就稍微学了一下游走问题。

NKOJ2285随机行走

随机行走:每一步,你有1/2的概率往左走一步,同时也有1/2的概率往右走一步。经过一段时间后,你期望到达的地点是0,也就是说,这样的随机行走的平均结果是你又回到了起点。 一个更有趣的问题是:在这样的行走中,往右期望能到达的最远处是哪里?(步数n<=1e3)   g[i][j]表示已经走了i步,当前坐标为-j,并且在这i步中打死不走到正数半轴的概率(n^2预处理) g[i][j] = R * g[i-1][j+1] + L * g[i-1][j-1] + (1-L-R) * g[i-1][j] (j!=0) g[i][0] = R * g[i-1][1] + (1-L-R)  *  g[i-1][0] f[i]表示已经走了i步,期望其答案大小 f[i] = sigma( (f[i-j-1]+1) * g[j]) * R (0<=j<=i-1) 那么对于这个递推式的意思即我们在i-j-1的地方迈到了上一个答案而花费j步到达当前新答案 而对于之后的前j-1步一直在原本的答案的地方及之左晃悠,而j-1步又回到了原点(原本答案的地方),而下一步向1进发,这个概率和我们 计算的R * g[j][0] (即j步之后保持在原地,这j步保持在负半轴和0,然后乘上这步往右迈的概率)应该是一样的。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define db double
using namespace std;
int n;
db L,R;
db g[1005][1005],f[1005];
int main() {
    scanf("%d%lf%lf",&n,&L,&R);
    g[0][0] = 1;
    for(int i=1;i<=n;i++) {
        g[i][0] = R*g[i-1][1] + (1-L-R)*g[i-1][0];
        for(int j=1;j<=i;j++){
            g[i][j] = R*g[i-1][j+1] + L*g[i-1][j-1] + (1-L-R)*g[i-1][j];        }
    }
    f[0] = 0;
    for(int i=1;i<=n;i++) {
        for(int j=0;j<=i-1;j++) {
            f[i] += ( f[i-j-1] + 1)*g[j][0]*R;
        }
    }
    printf("%.4lf",f[n]);
}

树上随机游走的期望距离最大点对

网上地址齐刷刷指向了jzoj orz orz orz orz,所以我并不能保证此代码正确。 设f[x]表示从x到达x父亲的期望游走距离,g[x]表示为x的父亲到达x的期望距离。期望距离具有可加性,即(x,y)的距离在他路径上一点z可以有(x,z)期望距离+(z,y)期望距离。 那么对于f[x]有两种情况讨论,x直接到x父亲,或x蹦蹦哒哒子树一圈回到x再上去。那么(我们设d[x]为x的度数) f[x] = (1/d[x]) * 1 + sigma( f[ son[x]  ] + 1 + f[x] )。 移项变换之后有 f[x] = d[x] + sigma f[son] 对于g[x]有三种情况,即father[x]跳到x的爷爷又回来,或者father[x]直接跳到x,或者跳到x的兄弟(但其实仔细想想跳到x爷爷或者兄弟本质是相同的)。 那么有g[x] = 1/d[x]  + 1/(d[fa[x]] * ( 1 + g[x] + g[fa[x]] ) +(1/d[fa[x]] )* sigma ( 1 +f[兄] + g[兄] )。 然后可以得到g[x] = g[fa[x]] + d[fa[x]] + sigma(f[x的兄弟])  . 通过观察可以发现f[x] 就是其子树所有节点的度数的和(神奇的变换)。 同时g[x] 就是n个节点中不属于x的子树的所有节点的度数和。 这样我们就可以求出来从树上一个点到另一个点的期望距离了。 而对于原题,我们枚举一下lca,维护一下up [ x ] (子树到达x的最长期望)和down[x](x到达子树的最长期望),动态更新一下子树信息和答案就可以了。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 200005;
int n;
int owo,en[maxn],nt[maxn],la[maxn];
void adg(int x,int y){
    en[++owo]=y; nt[owo]=la[x]; la[x]=owo;
}
int siz[maxn];
int ans;
int f[maxn],g[maxn],up[maxn],down[maxn];
//g[x] x's father to x
//f[x] x to x's father
void dfs(int x,int fr) {
    for(int it=la[x];it;it=nt[it]) {
        if(fr==en[it]) continue;
        dfs(en[it],x);
        siz[x] += siz[en[it]];
        ans = max(ans,max( up[x] + down[en[it]] + g[en[it]] , down[x] + up[en[it]] + f[en[it]] ));
        down[x] = max(down[x],g[en[it]]+down[en[it]]);
        up[x] = max(up[x],f[en[it]]+up[en[it]]);
    }
    siz[x] += 1;
    f[x] = 2*siz[x]-1;
    g[x] = 2*(n-siz[x])-1;
}
int main() {
    scanf("%d",&n);
    for(int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        adg(x,y); adg(y,x);
    }
    dfs(1,0);
    printf("%d",ans);
}

Luogu 3412 仓鼠找sugar II

其实我们如果能求出对于每一条边(我们设树上的一条边为两条有向边一条父亲向儿子,一条儿子向父亲),我们就是类似求出树上平均距离一样,对于每一条边都求出来他们的期望长度(也就是期望花费多久才能游走跃过那条边,这道题也就做出来了。 其实知道了上题就很裸了(其实我是为了做这道题,才来看的前面两道题orz) 我们单独考虑对于每一条边对于答案的贡献(乘上被几个点对覆盖),最后除以一下n^2就可以了。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 200005;
const int mod = 998244353;
int mul(int x,int y) {
    return 1ll*x*y%mod;
}
int add(int x,int y) {
    x+=y; return x>=mod?x-mod:x;
}
int inv(int x) {
    int ans = 1;
    int b = mod-2;
    for(;b;b>>=1,x=mul(x,x))
        if(b&1) ans = mul(ans,x);
    return ans;
}
int n;
int owo,en[maxn],nt[maxn],la[maxn];
void adg(int x,int y){
    en[++owo]=y; nt[owo]=la[x]; la[x]=owo;
}
int siz[maxn];
int ans;
int f[maxn],g[maxn],up[maxn],down[maxn];
//g[x] x's father to x
//f[x] x to x's father
void dfs(int x,int fr) {
    for(int it=la[x];it;it=nt[it]) {
        if(fr==en[it]) continue;
        dfs(en[it],x);
        siz[x]+=siz[en[it]];
    }
    siz[x] += 1;
    f[x] = 2*siz[x]-1;
    g[x] = 2*(n-siz[x])-1;
    for(int it=la[x];it;it=nt[it]) {
        if(fr==en[it]) continue;
        ans = add(ans,mul( mul(siz[en[it]],n-siz[en[it]] ),f[en[it]]));
        ans = add(ans,mul(mul(n-siz[en[it]],siz[en[it]]),g[en[it]]));
    }
}
int main() {
    scanf("%d",&n);
    for(int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        adg(x,y); adg(y,x);
    }
    dfs(1,0);
    int dd = inv(n);
    ans = mul(ans,mul(dd,dd));
    printf("%d",ans);
}
 
posted @ 2018-11-02 21:17  Newuser233  阅读(132)  评论(0)    收藏  举报