【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

*/

猜你喜欢

转载自www.cnblogs.com/ftotl/p/11856686.html