一道网上没有解题的题目,所以写一写留个纪念.
贴题
#1047 : Random Tree
时间限制:20000ms
单点时限:2000ms
内存限制:256MB
描述
给一无向带权完全图。我们等概率随机一棵生成树。求每两点间路径长度的期望。
输入
第一行一个整数n。 下面n行每行n个整数。第i行第j个整数ai, j为i到j的边的长度。
数据保证ai, i = 0且对于所有i, j有ai, j = aj, i。
2 ≤ n ≤ 1000
- 1000 ≤ ai, j ≤ 1000
输出
输出n行。每行包含n个实数。第i行第j个整数为i到j的路径的期望长度。
输出与标准答案相对误差或绝对误差不超过1e-9则视为正确。
完全图等概生成一颗生成树,求树上两点距离的期望矩阵.感觉有一定实际意义,可以用来衡量特定网络环境中两点距离
固定两点A,B
1.两个端点是A或B,这类边被取到的概率sigma(d)/(n-1)/n = 2/n
2.一个端点是A或B,1/n
3,没有端点是A或B,则A,B路径距离至少是3,考虑A,B路径长为k的中包含该类边e,假设e的两个端点为C,D
完全图中有n^(n-2)颗生成树,根据prufer编码可得
而A,B路径为k的路径的prufer编码有 A(n-4,k-3)*(k-2)*n^(n-k-2) * (k+1)种,
(除A,B,C,D外,A,B k路径中有k-3个点,排列A(n-4,k-3)*(k-2)对应不同A,B路径,给A,B标号最大,根据prufer编码,该树前(n-k-2)有n种编号,第n-k-1有k+1种编号,后面的固定)
扫描二维码关注公众号,回复:
5267650 查看本文章
比一下就可以得到概率,k从3循环到n-1求和可得到正确的答案.
#include <cstdio>
#pragma warning(disable:4996)
using namespace std;
const int bign = 1033;
double sum[2][bign][bign];
double a[bign][bign];
int n;
double ans[bign][bign];
long double A[bign];
double P[bign];
long double total = 0;
long double mypower(long double a, int b)
{
long double res = 1;
while (b)
{
if (b & 1)
res *= a;
a = a*a;
b >>= 1;
}
return res;
}
void init()
{
A[0] = 1;
A[1] = ((long double)n - 4)/n;
for (int k = 2; k <= n - 4; k++)
{
A[k] = A[k - 1] * (n - k - 3)/n;
}
}
int main()
{
scanf("%d", &n);
init();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
scanf("%lf", &a[i][j]);
sum[0][i][j] = sum[0][i][j - 1] + a[i][j];
sum[1][i][j] = sum[1][i - 1][j] + a[i][j];
total += a[i][j];
}
long double para = 0;
for (int k = 3; k <= n - 1; k++)
{
para += A[k - 3] * (k + 1) / n * (k - 2) / n / n ;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
long double tmp = (sum[0][i][n] + sum[1][n][j]) / n;
tmp += (total - 2 * (sum[0][i][n] + sum[1][n][j]) + 2 * a[i][j]) * para;
if (i == j)
tmp = 0;
printf("%.12Lf", tmp);
printf("%c", (j == n) ? '\n' : ' ');
}
}
}
/*
5
0 1 1 1 1
1 0 1 1 1
1 1 0 1 1
1 1 1 0 1
1 1 1 1 0
4
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
6
0 1 1 1 1 1
1 0 1 1 1 1
1 1 0 1 1 1
1 1 1 0 1 1
1 1 1 1 0 1
1 1 1 1 1 0
*/