【洛谷】P3211 [HNOI2011]XOR和路径

原题链接

题意

给定一张 \(n\) 个节点 \(m\) 条边且存在重边和自环的无向图,,一条路径的权值为路径上边权的异或和。

每个点可以等概率的走向相连的点。求从节点 \(1\) 出发第一次走到节点 \(n\) 时路径权值的期望。

\(1 \leq n \leq 100,1 \leq m \leq 10000\)

思路

根据异或各个位置相互独立的性质,可以考虑按位处理。

由于每个节点可以被经过多次,即假设 \(f_u\) 为从 \(1\) 走到 \(u\) 时当前位上为 \(1\) 的概率是无法转移的。由于最终点只会经过一次,可以假设 \(f_u\) 为从 \(u\) 节点开始走到 \(n\) 节点后当前位上为 \(1\) 的概率。

\(d_u\)\(u\) 节点的度数,不难得到状态的转移方程:

\[f_{u}=\dfrac{\sum_{(u,v)\in E \cap w_{u,v}=0} f_v+\sum_{(u,v)\in E \cap w_{u,v}=1} (1-f_v)}{d_u} \]

注意到状态转移可能成环,可以对原方程进行转化:

\[d_u\times f_u-\sum_{w_{u,v}=0} f_v+\sum_{w_{u,v}=1} f_v=\sum_{w_{u,v}=1} 1 \]

最终一共可以得到 \(n\) 个这样的方程,而一共有 \(n\) 个未知数,那么就可以用到高斯消元

最后,需要注意特判出现自环时,度数只需要 \(+1\)

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
const int M=20010;
const double eps=1e-9;
int n,m,h[N],idx,d[N];
double f[N][N],ans[N];
struct edge{int v,w,nex;}e[M];
void add(int u,int v,int w){e[++idx].v=v;e[idx].w=w;e[idx].nex=h[u];h[u]=idx;}
void NLCAKIOI()
{
	for(int i=1;i<=n;i++)
	{
		int r=i;
		for(int j=i+1;j<=n;j++)
		    if(f[j][i]-eps>=f[r][i]) r=j;
		if(r!=i) swap(f[i],f[r]);
		for(int j=n+1;j>=i;j--) f[i][j]/=f[i][i];
		for(int j=i+1;j<=n;j++)
			for(int k=n+1;k>=i;k--)
		        f[j][k]-=f[i][k]*f[j][i];
	}
	for(int i=n;i>=1;i--)
	{
		ans[i]=f[i][n+1];
		for(int j=i+1;j<=n;j++)
	    ans[i]-=f[i][j]*ans[j];
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int u,v,w,i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		if(u==v) add(u,v,w),d[v]++;
		else add(u,v,w),add(v,u,w),d[v]++,d[u]++;
	}
	double res=0;
	for(int s=0;s<30;s++)
	{
		memset(f,0,sizeof(f));f[n][n]=1;
		for(int u=1;u<n;u++)
		{
			f[u][u]=d[u];
			for(int i=h[u];i;i=e[i].nex)
			{
				int v=e[i].v;
				if((e[i].w>>s)&1) f[u][n+1]++,f[u][v]++;
				else f[u][v]--;
			}
		}
		NLCAKIOI();
		res+=(1<<s)*ans[1];
	}
	printf("%.3lf\n",res);
	return 0;
}
posted @ 2023-03-13 16:05  曙诚  阅读(29)  评论(0)    收藏  举报