BZOJ 2337 HNOI2011 XOR和路径 期望DP+高斯消元

题目大意:给定一个无向连通图,从1出发,每次等概率沿着任意一条出边走到n为止,求路径上的边权的异或和的期望值

首先既然是位运算的问题我们的一般处理办法就是拆位,按位处理

对于每一位 令f[i]为从i节点出发到n的期望值

对于每条出边,如果这条边边权为1,那么f[x]+=f[y]/d[x] 否则f[x]+=(1-f[y])/d[x] 其中d[x]表示x的度数

特殊地,f[n]=1

由于这个图不是拓扑图,因此我们用高斯消元来解这个方程组,n个方程n个未知数(代码里写SB了- -)

解出方程后f[1]*2^k就是第k位对答案的贡献值

统计答案输出即可 注意边集不要开小 重边只要连一条

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define M 110
using namespace std;
struct abcd{
	int to,f,next;
}table[M*M<<1];
int head[M],tot;
int n,m,degree[M];
long double f[M][M],a[M],ans;
void Add(int x,int y,int z)
{
	degree[x]++;
	table[++tot].to=y;
	table[tot].f=z;
	table[tot].next=head[x];
	head[x]=tot;
}
void Gauss_Elimination()
{
	int i,j,k;
	for(i=1;i<n;i++)
	{
		k=0;
		for(j=i;j<n;j++)
			if( fabs(f[j][i]) > fabs(f[k][i]) )
				k=j;
		for(j=i;j<=n+1;j++)
			swap(f[i][j],f[k][j]);
		for(j=i+1;j<n;j++)
		{
			long double temp=-f[j][i]/f[i][i];
			for(k=i;k<=n+1;k++)
				f[j][k]+=f[i][k]*temp;
		}
	}
	for(i=n-1;i;i--)
	{
		for(j=i+1;j<=n;j++)
			f[i][n+1]-=f[i][j]*a[j];
		a[i]=f[i][n+1]/f[i][i];
	}
}
int main()
{
	int i,j,x,y,z;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Add(x,y,z);
		if(x!=y) Add(y,x,z);
	}
	for(j=0;j<=30;j++)
	{
		memset(f,0,sizeof f);
		memset(a,0,sizeof a);
		for(x=1;x<n;x++)
		{
			for(i=head[x];i;i=table[i].next)
				if(table[i].f&(1<<j))
					f[x][table[i].to]+=1,f[x][n+1]+=1;
				else
					f[x][table[i].to]-=1;
			f[x][x]+=degree[x];
		}
		Gauss_Elimination();
		ans+=a[1]*(1<<j);
	}
	cout<<fixed<<setprecision(3)<<ans<<endl;
}


你可能感兴趣的:(高斯消元,bzoj,期望DP,BZOJ2337)