【概率和期望】有趣的随机游走问题
没有几天了呀,明天义冢的彭友们要到我们学校来虐我,瑟瑟发抖。概率和期望这块一直都觉得很难理解,感觉期望和概率还是十分重要的模块,就稍微学了一下游走问题。
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); }