【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

*/
posted @ 2019-11-14 14:06  孤攀客  阅读(131)  评论(0编辑  收藏  举报