【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;
}

 

posted @ 2017-06-20 20:19  GXZlegend  阅读(318)  评论(0编辑  收藏  举报