【BZOJ2337】XOR和路径(高斯消元)
大意
给出\(N\)个点,\(M\)条边的一张图,其中每条边都有一个非负整数边权。
一个人从1号点出发,在与该点相连的边中等概率的选择一条游走,直到走到\(N\)号点。
问:将这条路径上的边权异或起来的期望值为多少。
(图中可能有重边与自环)
思路
对于异或,我们考虑逐位解决,这样之后,边权只有0/1。
我们设\(Dp[u]\)表示从\(u\)到\(N\)路径的期望异或值为1时的概率。
那么对于除了\(N\)号点的每个点,我们将它相连的按边权边分为两类。
我们设边权为0的边相连的点为\(v0\),边权为1的边相连的点为\(v1\),
那么我们应该有\(Dp[u]=\sum \frac{Dp[v1]}{size}+\sum \frac{1-Dp[v2]}{size}\)
化一下简得到:\(Dp[u]-\sum \frac{Dp[v1]}{size}+\sum \frac{Dp[v2]}{size}=\frac{siz[v2]}{size}\),
即\(Dp[u]\cdot size-\sum{Dp[v1]}+\sum{Dp[v2]}=siz[v2]\)
这样的方程共有\(N-1\)个(除开\(N\)号点)。
而左边共有为(\(N-1\))个未知数(\(Dp[N]=0\))。
特殊的,对于\(N\)号点,我们需要特殊开一个方程:\(Dp[N]=0\).
这样,我们就可以用高斯消元来解决了。
而最终所求就是各个位时的相关贡献,总复杂度为\(O(N^3logV)\)。
代码
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define LD long double
const int MAXN=105;
int N,M;
LD Ans0;
LD Ans[MAXN];
LD A[MAXN][MAXN];
vector<int>P[MAXN],T[MAXN];
LD Abs(LD X){return X<0?-X:X;}
void GX(){
for(int j=1;j<=N;j++){
int Mx=j;
for(int i=j+1;i<=N;i++)
if(Abs(A[i][j])>Abs(A[Mx][j]))
Mx=i;
swap(A[Mx],A[j]);
for(int i=1;i<=N;i++){
if(i==j)continue;
LD tmp=A[i][j]/A[j][j];
A[i][j]=0;
for(int k=j+1;k<=N+1;k++)
A[i][k]-=A[j][k]*tmp;
}
}
for(int i=1;i<=N;i++)
Ans[i]=A[i][N+1]/A[i][i];
}
int main(){
scanf("%d%d",&N,&M);
for(int i=1,x,y,z;i<=M;i++){
scanf("%d%d%d",&x,&y,&z);
P[x].push_back(y);
T[x].push_back(z);
if(x==y)continue;//自环.
P[y].push_back(x);
T[y].push_back(z);
}
for(int k=0;k<=30;k++){
for(int i=1;i<N;i++){
int size=P[i].size();
for(int j=0;j<size;j++){
int v=P[i][j];
int val=(T[i][j]&(1<<k));
if(val)A[i][v]+=1,A[i][N+1]+=1;
else A[i][v]-=1;
}
A[i][i]+=size;
}
A[N][N]=1.0;GX();
Ans0+=(1<<k)*Ans[1];
memset(A,0,sizeof(A));
}
printf("%.3Lf\n",Ans0);
}
/*
2 2
1 1 2
1 2 3
*/