[HNOI 2011]XOR和路径

Description

给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数。试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。

直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 N 号节点为止,便得到一条从 1 号节点到 N 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR 和”的期望值。

Input

从文件input.txt中读入数据,输入文件的第一行是用空格隔开的两个正整数N和M,分别表示该图的节点数和边数。紧接着的M行,每行是用空格隔开的三个非负整数u,v和w(1≤u,v≤N,0≤w≤109),表示该图的一条边(u,v),其权值为w。输入的数据保证图连通,30%的数据满足N≤30,100%的数据满足2≤N≤100,M≤10000,但是图中可能有重边或自环。

Output

输出文件 output.txt 仅包含一个实数,表示上述算法得到的路径的“XOR 和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算)

Sample Input

2 2
1 1 2
1 2 3

Sample Output

2.333

HINT

样例解释:有1/2的概率直接从1号节点走到2号节点,该路径的“XOR和”为3;有1/4的概率从1号节点走一次1号节点的自环后走到2号节点,该路径的“XOR和”为1;有1/8的概率从1号节点走两次1号节点的自环后走到2号节点,该路径的“XOR和”为3;„„;依此类推,可知“XOR和”的期望值为:3/2+1/4+3/8+1/16+3/32+„„=7/3,约等于2.333。

题解

首先看到路径$xor$值,还是选择按位做。

我们设$f_u$表示从$u$到$n$的路径异或值为$1$的概率。显然$f_n == 0$。

此外,设$w(u, v)$为$u->v$的边权($1/0$),那么有:

$$f_u = \sum_{(u,v) \in E,\ w(u,v) = 0} \frac{f_v}{degree_u} + \sum_{(u,v) \in E,\ w(u,v) = 1} \frac{1-f_v}{degree_u}$$

那么我们可以得到$n$个方程,用高斯消元求解。

可以乘上$degree_u$减小误差。

这题特殊说明一下为什么不能顺推而要逆推:

很多题解的说法是因为“如果正推的话,$1−f_i$代表的不仅从$1$到$i$异或和不为$1$的概率,还包含了从$1$不走到$i$的概率,无法转移”。

如果这样解释,那就解释不了$i$走不到$n$的情况。

我认为合理的解答是:因为$1$可以重复走多次,而$n$只能走$1$次。

 1 //It is made by Awson on 2017.10.21
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime>
 6 #include <stack>
 7 #include <queue>
 8 #include <vector>
 9 #include <string>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 #define LL long long
16 #define Min(a, b) ((a) < (b) ? (a) : (b))
17 #define Max(a, b) ((a) > (b) ? (a) : (b))
18 #define Abs(x) ((x) < 0 ? (-(x)) : (x))
19 using namespace std;
20 const int N = 100;
21 const int M = 10000;
22 int st[32];
23 
24 int n, m, u, v, w;
25 struct tt {
26     int to, cost, next;
27 }edge[(M<<1)+5];
28 int path[N+5], top, degree[N+5];
29 double A[N+5][N+5], ans;
30 
31 double Gauss() {
32     for (int line = 1; line <= n; line++) {
33         int max_line = line;
34         for (int i = line+1; i <= n; i++) if (fabs(A[i][line]) > fabs(A[max_line][line])) max_line = i;
35         if (max_line != line) swap(A[line], A[max_line]);
36         for (int i = line+1; i <= n; i++) {
37             double div = A[i][line]/A[line][line];
38             for (int j = line; j <= n+1; j++) A[i][j] -= A[line][j]*div;
39         }
40     }
41     for (int i = n; i >= 1; i--) {
42         for (int j = i+1; j <= n; j++)
43             A[i][n+1] -= A[i][j]*A[j][n+1];
44         A[i][n+1] /= A[i][i];
45     }
46     return A[1][n+1];
47 }
48 void add(int u, int v, int w) {
49     edge[++top].to = v;
50     edge[top].cost = w;
51     edge[top].next = path[u];
52     path[u] = top; degree[v]++;
53 }
54 void work() {
55     st[0] = 1; for (int i = 1; i <= 30; i++) st[i] = st[i-1]<<1;
56     scanf("%d%d", &n, &m);
57     for (int i = 1; i <= m; i++) {
58         scanf("%d%d%d", &u, &v, &w);
59         add(u, v, w); if (u != v) add(v, u, w);
60     }
61     for (int i = 0; i <= 30; i++) {
62         memset(A, 0, sizeof(A));
63         for (int u = 1; u < n; u++) {
64             A[u][u] = degree[u];
65             for (int j = path[u]; j; j = edge[j].next) {
66                 if (st[i]&edge[j].cost) A[u][edge[j].to] += 1., A[u][n+1] += 1.;
67                 else A[u][edge[j].to] -= 1.;
68             }
69         }
70         A[n][n] = 1;
71         ans += Gauss()*(double)st[i];
72     }
73     printf("%.3lf\n", ans);
74 }
75 int main() {
76     work();
77     return 0;
78 }

 

posted @ 2017-10-21 23:32  NaVi_Awson  阅读(368)  评论(0编辑  收藏  举报