#1047 : Random Tree(概率+prufer计数+卡精度)

http://hihocoder.com/problemset/problem/1047

题解:

  • 考虑枚举一条边\((x,y)\),看它在\((u,v)\)路径上的概率是多少?

  • \((x,y)=(u,v)\),不难算出概率即是\((x,y)\)在生成树上的概率,即\(\frac{n-1}{n*(n-1)/2}=\frac{2}{n}\)

  • \((x,y)\)\((u,v)\)有一个公共点时,假设是\(x=u\),发现问题转为\(v\)\(y\)的子树里的概率,显然是\(\frac{1}{2}\),再乘上\(\frac{2}{n}\)即可。

  • \((x,y)\)\((u,v)\)没有交集时,大胆猜想概率是\(\frac{1}{2}\),可惜它错了。

  • 此时把问题转一转,因为所有的这样的边都是一样的,变成求\((u,v)\)的期望长度,再除掉可能的边数即可。

  • \(P((x,y)在(u,v)上)=\frac{\sum_{i=4}^{n} ~ P((u,v)路径点数=i)*(i-3)}{n*(n-1)/2-2(n-1)+1}\)

  • \(P((u,v)路径点数=i)\)可以用方案数除以总方案数\(n^{n-2}\)得到。

  • \(F(n,m)\)等于\(n\)个点,\(m\)个森林,\(1..m\)属于不同森林的方案数,可以视作一开始一个\(m\)大小的联通块,由生成树计数\(\sum_{prufer} \prod_{联通块x}siz[x]^{x的prufer序列中的出现次数+1}\)

  • 即可得到公式:\(F(n,m)=m*n^{n-1-m}\)

  • 方案数\(=F(n,i)*P(n-2,i-2)\)

  • 卡精度,拆式子得:\(\sum_{i=4}^n \frac{(n-2)!}{(n-i)!*n^{i-1}}*i*(i-3)\),可以倒着枚举\(i\),然后每次乘一个数除以一个数。

  • 需要用long double精度才够。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

#define db long double

const int N = 1005;

int n;
db a[N][N], b[N];

db calc(int n) {
	if(n <= 3) return 0;
	db ans = 0, s = 1;
	fo(i, 1, n - 2) s = s * i / n;
	s /= n;
	ans += s * n * (n - 3);
	fd(i, n - 1, 4) {
		s *= n;
		s /= (n - i);
		ans += s * i * (i - 3);
	}
	return ans;
}

int main() {
	scanf("%d", &n);
	fo(i, 1, n) fo(j, 1, n) scanf("%Lf", &a[i][j]), b[i] += a[i][j];
	db sum = 0;
	fo(i, 1, n) sum += b[i];
	sum /= 2;
	db v = calc(n);
	if(n > 3) v /= n * (n - 1) / 2 - 2 * (n - 1) + 1;
	fo(i, 1, n) {
		fo(j, 1, n) {
			db v2 = sum - b[i] - b[j] + a[i][j];
			db ans = v2 * v + (b[i] + b[j] - 2 * a[i][j]) / n + a[i][j] * 2 / n;
			if(i == j) ans = 0;
			pp("%.9Lf\n", ans);
		}
	}
}
posted @ 2020-06-06 15:00  Cold_Chair  阅读(258)  评论(0编辑  收藏  举报