1. 程式人生 > 其它 >隨機遊走 / T1(期望)(樹形DP)

隨機遊走 / T1(期望)(樹形DP)

給你一個樹,問你對於每個點對 (i,j),i 走到 j 的期望步數的最大值。 行走的方式是在可以一步到達的點中等概率的選擇一個走過去。

隨機遊走 / T1

題目大意

給你一個樹,問你對於每個點對 (i,j),i 走到 j 的期望步數的最大值。
行走的方式是在可以一步到達的點中等概率的選擇一個走過去。

思路

首先我們觀察樣例,發現如果一個點是葉子節點(或者說度數是 \(1\)),那它到它父親(那條邊連到的點)的期望步數一定是 \(1\)

考慮從這個作為最初始的狀態,然後 DP。(\(d_i\) 是點 \(i\) 的度數)
然後就考慮求出以 \(1\) 為根的時候,每個點到它父親的期望步數。
然後可以得出:\(f_i=\dfrac{1}{d_i}+\sum\limits_{j=son_i}\dfrac{1+f_j+f_i}{d_i}\)


然後通過移項可以得到 \(f_i=d_i+\sum\limits_{j=son_i}f_j\)

然後你就可以 \(O(n)\) 求,然後我們考慮求從父親到它的。
那你考慮 \(i\) 到它父親和它父親到 \(i\) 的期望步數之間的關係。

那它父親到它套進前面的方程,就是 \(\sum\limits_{j在i的子樹內}\{d_j\}\)
然後你考慮每個 \(d_i\) 被計算多少次,會發現從它到父親,除了它到它父親的邊值計算了一次,下面的邊都是計算了兩次。
然後父親到它也是同一個道理。
那不難想到總次數分別是 \(2*sz_i-1,2*(n-sz_i)-1\),那加在一起,就變成了 \(2*n-2\)

\(2*(n-1)\)
所以你就可以通過讓 \(2*(n-1)\) 減去它到父親的期望步數,得到父親到它的期望步數。

自此,\(n^2\) 的做法已經可以了。
那我們考慮 \(O(n)\)

考慮列舉你選的兩個點的 LCA。
那我們可以預處理 DP 求出 \(a_i,b_i\),分別代表 \(i\) 到它子樹裡面的點的最長期望距離和它子樹裡面的點到它的最長期望距離。
然後接著對於每個點,我們把它的每個兒子的 \(a,b\) 值都拿出來。
然後列舉兒子,就有 \(f_j=b_j+f_{j,i}+\max\limits_{k=son_i,k\neq j}\{a_k+f_{i,k}\}\)
然後這個維護一下字首字尾最大值即可。

然後就可以了。

程式碼

#include<cstdio>
#include<iostream>

using namespace std;

struct node {
	int to, nxt;
}e[200001];
int n, x, y, le[100001], KK, tmpn;
int fa[100001], du[100001], pl[100001];
double ans, f[100001], a[100001], b[100001];
double tmp[100001], q[100002], h[100002];

void add(int x, int y) {
	e[++KK] = (node){y, le[x]}; le[x] = KK;
	e[++KK] = (node){x, le[y]}; le[y] = KK; 
}

void dfs1(int now, int father) {//第一個 dfs 求出 i 走到 fai 的期望步數
	f[now] = du[now];
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father) {
			dfs1(e[i].to, now);
			f[now] += f[e[i].to];
		}
}

void dfsab(int now, int father) {//得出轉移用的 ab 陣列
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father) {
			dfsab(e[i].to, now);
			a[now] = max(a[now], a[e[i].to] + 2 * (n - 1) - f[e[i].to]);
			b[now] = max(b[now], b[e[i].to] + f[e[i].to]);
		}
}

void dfsans(int now, int father) {//對於每個 LCA 求值
	tmpn = 0;
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father) {
			tmp[++tmpn] = a[e[i].to] + 2 * (n - 1) - f[e[i].to];
			pl[tmpn] = e[i].to;
		}
	ans = max(ans, max(a[now], b[now]));
	for (int i = 1; i <= tmpn; i++)
		q[i] = max(q[i - 1], tmp[i]);
	for (int i = tmpn; i >= 1; i--)
		h[i] = max(q[i + 1], tmp[i]);
	for (int i = 1; i <= tmpn; i++)
		ans = max(ans, b[pl[i]] + f[pl[i]] + max(q[i - 1], h[i + 1]));
	
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father) {
			dfsans(e[i].to, now);
		}
}

int main() {
//	freopen("rw.in", "r", stdin);
//	freopen("rw.out", "w", stdout);
	
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d %d", &x, &y);
		add(x, y); du[x]++; du[y]++;
	}
	
	dfs1(1, 0);
	dfsab(1, 0);
	dfsans(1, 0);
	
	printf("%.5lf", ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}