BZOJ 3270: 博物馆

Description

有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆。这座博物馆有着特别的样式。它包含由m条走廊连接的n间房间,并且满足可以从任何一间房间到任何一间别的房间。

两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)

不过,尽管他们到处乱跑,但他们还没有看完足够的艺术品,因此他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),有1-Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。

两个男孩同时行动。由于走廊很暗,两人不可能在走廊碰面,不过他们可以从走廊的两个方向通行。(此外,两个男孩可以同时地穿过同一条走廊却不会相遇)两个男孩按照上述方法行动直到他们碰面为止。更进一步地说,当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。

两个男孩现在分别处在a,b两个房间,求两人在每间房间相遇的概率。

Input

第一行包含四个整数,n表示房间的个数;m表示走廊的数目;a,b (1 ≤ a, b ≤ n),表示两个男孩的初始位置。

之后m行每行包含两个整数,表示走廊所连接的两个房间。

之后n行每行一个至多精确到小数点后四位的实数 表示待在每间房间的概率。

题目保证每个房间都可以由其他任何房间通过走廊走到。

Output

输出一行包含n个由空格分隔的数字,注意最后一个数字后也有空格,第i个数字代表两个人在第i间房间碰面的概率(输出保留6位小数)

注意最后一个数字后面也有一个空格

Sample Input

2 1 1 2

1 2

0.5

0.5

Sample Output

0.500000 0.500000

HINT

对于100%的数据有 n <= 20,n-1 <= m <= n(n-1)/2

Source

高斯消元

Solution

有一个非常妙的东西,就是在博物馆i碰头的概率=两个人都在博物馆i的期望次数/总碰头次数,这题总碰头次数=1,因此两个人都在i的期望次数就是答案。

记\(f_{i,j}\)表示A在i,B在j的期望次数。

列出转移,放到矩阵中 用神奇的高斯去消。

注意\(f_{S,T}\)要多加1,因为一开始算一次。

和的期望等于期望的和。

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
const double one = 1;
const double eps = 1e-10;
double a[405][405], P[25];
int n, m, S, T, tot, cnte, h[25], id[25][25], D[25];
struct edge_t { int to, next; } edge[100005];
inline int getint() {
	int r = 0; bool z = true; char c = getchar();
	for (; c < '0' || c > '9'; c = getchar()) if (c == '-') z = false;
	for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
	if (z) return r;
	return -r;
}
void ins(int x, int y) {
	edge[++cnte].to = y;
	edge[cnte].next = h[x];
	h[x] = cnte;
}
void build(int u, int v) {
	int x = id[u][v], y; --a[x][x];
	for (int i = h[u]; i; i = edge[i].next)
		for (int j = h[v]; j; j = edge[j].next) {
			int tu = edge[i].to, tv = edge[j].to;
			if (tu == tv) continue; y = id[tu][tv];
			if (tu == u && tv == v) a[x][y] += P[tu] * P[tv];
			else if (tu == u) a[x][y] += P[tu] * (one - P[tv]) / double(D[tv]);
			else if (tv == v) a[x][y] += P[tv] * (one - P[tu]) / double(D[tu]);
			else a[x][y] += (one - P[tu]) * (one - P[tv]) / double(D[tu]) / double(D[tv]);
		}
}
void Gauss() {
	int pos;
	for (int i = 1; i < tot; ++i) {
		for (int j = i; j <= tot; ++j) if (fabs(a[j][i]) > eps) {pos = j; break;}
		if (i != pos) for (int j = 1; j <= tot+1; ++j) swap(a[i][j], a[pos][j]);
		for (int j = 1; j <= tot; ++j) {
			if (j == i || fabs(a[j][i]) <= eps) continue;
			double tmp = a[j][i] / a[i][i];
			for (int k = 1; k <= tot + 1; ++k) a[j][k] -= tmp * a[i][k];
		}
	}
}
int main() {
	n = getint(); m = getint(); S = getint(); T = getint();
	for (int i = 0; i < m; ++i) {
		int x = getint(), y = getint();
		ins(x, y); ins(y, x);
		++D[x]; ++D[y];
	}
	for (int i = 1; i <= n; ++i) {
		ins(i, i);
		scanf("%lf", P + i);
		for (int j = 1; j <= n; ++j) id[i][j] = ++tot;
	}
	a[id[S][T]][tot+1] = -one;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			build(i, j);
	Gauss();
	for (int i = 1; i <= n; ++i) {
		int x = id[i][i];
		printf("%.6lf ", a[x][tot+1] / a[x][x]);
	}
}