洛谷 P3211 [HNOI2011] XOR和路径
题目传送门
思路
哇,概率期望!
哇,图上游走!
哇,\(n \leq 100\)!
高斯消元没跑了。
1. 状态与转移
求期望一般用倒推,设 \(f_u\) 表示从 \(u\) 节点到 \(n\) 节点的异或路径和的期望。
直接列方程:
\[f_u = \frac{1}{d_u} \sum_{(u, v) \in E} w_{u, v} \bigoplus f_v
\]
其中 \(d_u\) 就表示 \(u\) 节点的度。
整个方程的含义就是从 \(u\) 节点出发,会有 \(\frac{1}{d_u}\) 的概率走到相邻的一个节点 \(v\)。因为从 \(u\) 走到 \(v\) 再走到 \(n\) 的异或期望为 \(w_{u, v} \bigoplus f_v\),所以此种情况的贡献就是 \(\frac{1}{d_u} \times (w_{u, v} \bigoplus f_v)\)。
由于有位运算,而且还是与浮点数 \(f\) 进行位运算,好像不太能转化成高斯消元的样子。。。
难道要燃尽了吗?
经过一番学习 (看题解) 之后,终于意识到期望是可以进行线性运算,即:
\[E(X + Y) = E(X) + E(Y),
\]
\[E(X + c) = E(X) + c,
\]
\[E(k \times x) = k \times E(x),
\]
\[E(aX + bY) = aE(x) + bE(y).
\]
其中 \(X,Y\) 是两个时间的结果大小,\(k, a, b, c\) 均为常数。
由于位运算时,每一位都是互不干扰的,所以总的期望就可以通过线性期望来拆解。
因此,我们对边的每一位进行一次高斯消元,第 \(i\) 次消元的每条边的边权就是 \(w \& (1 << i)\)。
我们设 \(f_u\) 表示从 \(u\) 节点走到 \(n\) 节点时,异或和为 \(1\) 的期望(其实就是概率),那就可以列出转移方程:
\[f_u = \frac{1}{d_u} [\sum_{w_{u, v} \& (1<<i) = 0} f_v + \sum_{w_{u, v} \& (1<<i) = 1} (1-f_v)]
\]
意思如下:
- 如果该边边权为 \(0\),那么走过去我的异或和就不会改变,该边对 \(u\) 节点的贡献就是【\(v\) 到 \(n\) 节点的期望 \(\times \frac{1}{d_u}\)】;
- 如果该边边权为 \(1\),那么走过去我的异或和就会改变,那么由于 \(v\) 到 \(n\) 节点异或和为 \(1\) 的概率为 \(f_v\),那么它到 \(n\) 的异或和为 \(0\) 的概率为 \(1 - f_v\),走过这条边后,\(0\) 变 \(1\)、\(1\) 变 \(0\),概率反转,\(u\) 走到 \(n\) 异或和概率为 \(1 - f_v\),再乘以 \(\frac{1}{d_u}\) 就是这条变的贡献。
最后将其化为高斯消元的形式即可。
2. 答案
\[ans = \sum_{i = 0}^{30} 2^i \times f_1
\]
3.复杂度
- 时间:有系数矩阵的预处理,所以是 \(O(30 \times (n \times m + n ^ 3))\);
- 空间:\(O(n^2)\)。
代码
#include <bits/stdc++.h>
#define mkpr make_pair
#define fir first
#define sec second
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e2 + 7;
int n, m;
double a[maxn][maxn];
vector<pii> e[maxn];
// 预处理系数矩阵
void init(int pos) {
memset(a, 0, sizeof(a));
for (int u = 1; u < n; ++u) {
a[u][u] = e[u].size();
for (pii ed : e[u]) {
int v = ed.fir, w = ed.sec;
if (w & (1 << pos))
a[u][v] += 1.0, a[u][n + 1] += 1.0;
else a[u][v] -= 1.0;
}
}
a[n][n] = 1;
}
void Guass_Jordan() {
for (int r = 1, c = 1; c <= n; ++r) {
int maxr = r;
for (int i = r + 1; i <= n; ++i)
if (fabs(a[maxr][c]) < fabs(a[i][c])) maxr = i;
if (maxr != r) swap(a[maxr], a[r]);
if (fabs(a[r][c]) < 1e-8) {--r, ++c; continue;}
for (int j = n + 1; j >= c; --j)
a[r][j] /= a[r][c];
for (int i = 1; i <= n; ++i) {
if (i == r) continue;
for (int j = n + 1; j >= c; --j)
a[i][j] -= a[r][j] * a[i][c];
}
++c;
// puts("----------------------------------------");
// for (int i = 1; i <= n; ++i, printf("\n"))
// for (int j = 1; j <= n + 1; ++j)
// printf("%.6lf ", a[i][j]);
}
}
double ans;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
e[u].push_back(mkpr(v, w));
if (u != v) e[v].push_back(mkpr(u, w));
}
for (int i = 0; i <= 30; ++i) {
init(i);
// puts("===========================================");
// printf("i: %d\n", i + 1);
// for (int j = 1; j <= n; ++j, printf("\n"))
// for (int k = 1; k <= n + 1; ++k)
// printf("%.6lf ", a[j][k]);
Guass_Jordan();
ans += (1 << i) * a[1][n + 1];
// printf("f1 = %.6lf\n", a[1][n + 1]);
}
printf("%.3lf\n", ans);
return 0;
}

浙公网安备 33010602011771号