【bzoj2337】[HNOI2011]XOR和路径 期望dp+高斯消元
题目描述
题解
期望dp+高斯消元
直接求总体的期望异或和比较困难,我们可以考虑按位拆分一下,这样每一位就只有0/1两种选择。
设f[i]表示从i到n的期望异或和,那么$f[y]=\sum\limits_{exist\ x1\to y=0}\frac{f[x1]}{d[x1]}+\sum\limits_{exist\ x2\to y=1}\frac{1-f[x2]}{d[x2]}$,其中x1->y=0表示x1到y有权值为0的边,x2->y=1表示x2到y有权值为1的边。
这里和 bzoj3143 的处理类似,同样需要特殊处理n,令f[n]=0。
不过这道题最恶心之处在于它有重边和自环,对于重边需要把"="变为"+=",对于自环,按照样例的意思应该是把自环边的度数看作1而不是2处理(两种方式走自环算一种)
然后使用高斯消元求解,将f[1]乘上拆分的位加到答案中。
时间复杂度$O(n^3\log n)$
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #define N 110 #define M 20010 using namespace std; int n , m , head[N] , to[M] , len[M] , next[M] , cnt , d[N] , x[M] , y[M] , z[M]; double a[N][N]; double cal(int t) { int i , j , k; double f; memset(a , 0 , sizeof(a)); for(i = 1 ; i <= m ; i ++ ) { if(z[i] & t) { a[x[i]][y[i]] += 1 , a[x[i]][n + 1] += 1; if(x[i] != y[i]) a[y[i]][x[i]] += 1 , a[y[i]][n + 1] += 1; } else { a[x[i]][y[i]] -= 1; if(x[i] != y[i]) a[y[i]][x[i]] -= 1; } } for(i = 1 ; i <= n + 1 ; i ++ ) a[n][i] = 0; for(i = 1 ; i <= n ; i ++ ) a[i][i] += d[i]; for(i = 1 ; i <= n ; i ++ ) { for(k = i , j = i + 1 ; j <= n ; j ++ ) if(fabs(a[j][i]) > fabs(a[k][i])) k = j; for(j = i ; j <= n + 1 ; j ++ ) swap(a[k][j] , a[i][j]); for(j = i + 1 ; j <= n ; j ++ ) for(f = a[j][i] / a[i][i] , k = i ; k <= n + 1 ; k ++ ) a[j][k] -= a[i][k] * f; } for(i = n ; i ; i -- ) { for(j = i + 1 ; j <= n ; j ++ ) a[i][n + 1] -= a[i][j] * a[j][n + 1]; a[i][n + 1] /= a[i][i]; } return a[1][n + 1]; } int main() { int i; double ans = 0; scanf("%d%d" , &n , &m); for(i = 1 ; i <= m ; i ++ ) { scanf("%d%d%d" , &x[i] , &y[i] , &z[i]) , d[x[i]] ++ ; if(x[i] != y[i]) d[y[i]] ++ ; } for(i = 1 << 30 ; i ; i >>= 1) ans += cal(i) * i; printf("%.3lf\n" , ans); return 0; }