题目描述
题解
期望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;
}