1. 程式人生 > >BJ模擬 隨機遊走【期望dp+倍增】

BJ模擬 隨機遊走【期望dp+倍增】

題目描述

給定一棵n個節點的樹,一個人在樹上隨機遊走,即從一個點等概率走到相鄰的一個點,m組詢問,問從x走到y的期望。
n100000

解題思路:

樹上概率期望一般設兩個值,一個從自己到父親,一個從父親到自己。
fi表示從i走到fa[i]的期望步數,k=degi,則:

fi=1k+1k(1+fson[i]+fi)

即:fi=k+1kfson[i]

gi表示從fa[i]走到i的期望步數,k=degfa[i]則:

gi=1k+1k(1+fbrother[i]+gi)+1k(1+gfa[i]+gi)

即:gi=k+fbrother[i]+gfa[i]=ffa[i]fi+gfa[i]

dfs求出fg後,詢問答案即為fx>lca+gy>lca,倍增預處理和即可。

#include<bits/stdc++.h>
#define ll long long
using namespace
std; int getint() { int i=0,f=1;char c; for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar()); if(c=='-')c=getchar(),f=-1; for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0'; return i*f; } const int N=100005; int n,m,dep[N],du[N],fa[N][20]; int tot,first[N],nxt[N<<1
],to[N<<1]; ll f[N][20],g[N][20]; void add(int x,int y) { nxt[++tot]=first[x],first[x]=tot,to[tot]=y,du[y]++; } void dfs1(int u) { f[u][0]=du[u]; for(int i=1;i<20;i++)fa[u][i]=fa[fa[u][i-1]][i-1]; for(int e=first[u];e;e=nxt[e]) { int v=to[e]; if(v==fa[u][0])continue; fa[v][0]=u,dep[v]=dep[u]+1; dfs1(v);f[u][0]+=f[v][0]; } } void dfs2(int u) { for(int i=1;i<20;i++)f[u][i]=f[u][i-1]+f[fa[u][i-1]][i-1]; for(int i=1;i<20;i++)g[u][i]=g[u][i-1]+g[fa[u][i-1]][i-1]; for(int e=first[u];e;e=nxt[e]) { int v=to[e]; if(v==fa[u][0])continue; g[v][0]=f[u][0]+g[u][0]-f[v][0]; dfs2(v); } } void solve(int x,int y) { ll ans=0; if(dep[x]>dep[y]) { int det=dep[x]-dep[y]; for(int i=0;i<20;i++) if(det>>i&1)ans+=f[x][i],x=fa[x][i]; } else if(dep[y]>dep[x]) { int det=dep[y]-dep[x]; for(int i=0;i<20;i++) if(det>>i&1)ans+=g[y][i],y=fa[y][i]; } for(int i=19;i>=0;i--)if(fa[x][i]!=fa[y][i]) { ans+=f[x][i]+g[y][i]; x=fa[x][i],y=fa[y][i]; } if(x!=y)ans+=f[x][0]+g[y][0]; printf("%lld\n",ans); } int main() { //freopen("lx.in","r",stdin); int x,y; n=getint(); for(int i=1;i<n;i++) { x=getint(),y=getint(); add(x,y),add(y,x); } dfs1(1),g[1][0]=0,dfs2(1); m=getint(); while(m--) { x=getint(),y=getint(); solve(x,y); } return 0; }