#数学期望+lca#[ssloj 1457] 树(tree)

Title

题目描述
梦游中的你来到了一棵 N 个节点的树上. 你一共做了 Q 个梦, 每个梦需要你从点 u 走到 点 v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择一条走过去, 为了确保第二天能够准时到校, 你要求出每个梦期望经过多少条边才能苏 醒. 为了避免精度误差, 你要输出答案模10^9 + 7的结果.

输入
第一行两个整数分别代表 N 和 Q. 接下来 N-1 行, 每行两个整数 u, v 代表树中的一条边. 接下来 Q 行, 每行两个整数代表询问的 u,v.


Solution

随机游走问题,貌似跟之前做过的期望不一样。
f [ i ] f[i] f[i]表示由 i i i走向父亲的期望距离, d [ i ] d[i] d[i]为点 i i i的度,设 g [ i ] g[i] g[i]表示由 i i i的父亲走向 i i i的期望距离
f [ i ] = 1 d [ i ] + ∑ j ∈ s o n i f [ i ] + f [ j ] + 1 d [ x ] f[i]=\frac{1}{d[i]}+\sum_{j\in son_i}\frac{f[i]+f[j]+1}{d[x]} f[i]=d[i]1+jsonid[x]f[i]+f[j]+1
f [ i ] = d [ i ] + ∑ j ∈ s o n i f [ j ] f[i]=d[i]+\sum_{j\in son_i}f[j] f[i]=d[i]+jsonif[j]
g [ i ] = 1 d [ f a [ i ] ] + g [ i ] + g [ f a [ i ] ] + 1 d [ f a [ i ] ] + ∑ j ∈ s o n f a [ i ] ∩ j ≠ i g [ i ] + f [ j ] + 1 d [ f a [ i ] ] g[i]=\frac{1}{d[fa[i]]}+\frac{g[i]+g[fa[i]]+1}{d[fa[i]]}+\sum_{j\in sonfa[i]\cap j\neq i}\frac{g[i]+f[j]+1}{d[fa[i]]} g[i]=d[fa[i]]1+d[fa[i]]g[i]+g[fa[i]]+1+jsonfa[i]j=id[fa[i]]g[i]+f[j]+1
g [ y ] = f [ x ] − f [ y ] + g [ x ] g[y]=f[x]-f[y]+g[x] g[y]=f[x]f[y]+g[x]


Code

#include<cstdio>
#include<algorithm>
#define mod 1000000007
#define rep(i,x,y) for(int i=x;i<=y;++i)
using namespace std; 
const int N=100010; 
struct node{
	int y,next; 
}a[N<<1];
int tot,head[N],n,Q,f[N],g[N],dep[N],ff[N][18],deg[N]; 
void add(int x,int y){
	a[++tot]=(node){y,head[x]}; head[x]=tot; deg[x]++; 
}
void dfs1(int x,int fa){
	dep[x]=dep[fa]+1; f[x]=deg[x]; 
	for(int i=head[x];i;i=a[i].next){
		int y=a[i].y; 
		if (y!=fa){
			ff[y][0]=x; 
			dfs1(y,x); 
			f[x]+=f[y]; 
		}
	}
	return; 
}
void dfs2(int x,int fa){
	for(int i=head[x];i;i=a[i].next){
		int y=a[i].y; 
		if (y!=fa){
			g[y]=f[x]-f[y]+g[x]; 
			dfs2(y,x); 
		}
	}
	return; 
}
void dfs3(int x,int fa){
	for(int i=head[x];i;i=a[i].next){
		int y=a[i].y; 
		if (y!=fa){
			f[y]+=f[x]; g[y]+=g[x]; 
			dfs3(y,x); 
		}
	}
	return; 
}
inline int lca(int x,int y){
	if (dep[x]<dep[y]) x^=y^=x^=y; 
	for(int i=17;i>=0;i--) if (dep[x]-(1<<i)>=dep[y]) x=ff[x][i]; 
	if (x==y) return x; 
	for(int i=17;i>=0;i--) if (ff[x][i]!=ff[y][i]) x=ff[x][i],y=ff[y][i]; 
	return ff[x][0]; 
}
int main(){
	scanf("%d%d",&n,&Q); 
	rep(i,1,n-1) {
		int x,y; 
		scanf("%d%d",&x,&y); 
		add(x,y),add(y,x); 
	}
	dfs1(1,0); dfs2(1,0); f[1]=g[1]=0; dfs3(1,0); 
	rep(j,1,17) rep(i,1,n) ff[i][j]=ff[ff[i][j-1]][j-1]; 
	
	while(Q--){
		int x,y,z; 
		scanf("%d%d",&x,&y); z=lca(x,y); 
		printf("%d\n",f[x]-f[z]+g[y]-g[z]); 
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值