初见安~这里是传送门:洛谷P3211 [HNOI2011]XOR和路径
题解
因为是异或和,所以不能直接用\sum来表示这个式子。但是可以先二进制拆分一下,依次处理每一位的边权异或和的贡献,所以当前的问题就是在一个边权只有0或1的图里面求异或和期望。其实已经可以大概表示出来了——设表示从u到n路径这一位为1的期望,则就是这一位为0的期望,所求为。那么就可以考虑转移了:
移项一下:
到这里好像已经化不动了。但我们可以注意到数据范围n很小,只有100,可以。所以有一个新的做法就是——高斯消元。
这就是一个n元1次方程了。带入系数算即可。
上代码——
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#define maxn 105
#define maxm 10005
using namespace std;
typedef long long ll;
typedef long double ld;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
struct edge {int to, w, nxt;} e[maxm << 1];
int head[maxn], K = 0;
void add(int u, int v, int w) {e[K] = {v, w, head[u]}; head[u] = K++;}
int n, m;
int deg[maxn];
ld p[maxn][maxn], ans = 0.0;
void gauss() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n + 1; j++) if(j != i) p[i][j] /= p[i][i];
p[i][i] = 1;
for(int j = 1; j <= n; j++) if(j != i) {
for(int k = 1; k <= n + 1; k++) if(k != i)
p[j][k] -= p[j][i] * p[i][k];
p[j][i] = 0;
}
}
}
signed main() {
n = read(), m = read();
memset(head, -1, sizeof head);
for(int i = 1, u, v, w; i <= m; i++) {
u = read(), v = read(), w = read(), add(u, v, w), deg[v]++;
if(u != v) add(v, u, w), deg[u]++;//deg是边数。
}
for(int i = 30; i >= 0; i--) {//枚举每一位
memset(p, 0, sizeof p);
for(int u = 1; u < n; u++) {
p[u][u] = deg[u];//对应系数 n+1是等式右边
for(int j = head[u], v; ~j; j = e[j].nxt) {
v = e[j].to; if(e[j].w & (1 << i)) p[u][n + 1] += 1, p[u][v] += 1;
else p[u][v] -= 1;
}
}
p[n][n] = 1;//因为是到点n的期望,所以点n自己为1的期望为0,但是系数需要为1。
gauss();
ans += (1 << i) * p[1][n + 1];
}
printf("%.3Lf\n", ans);
return 0;
}
迎评:)
——End——