【HNOI 2011】XOR和路径

本文介绍了一种解决特定图论问题的方法,即如何在一个无向连通图中找到从节点1到节点n的路径,使得路径上的边权值XOR和最大。通过概率模型和高斯消元法,文章详细解释了如何计算这一路径XOR和的期望值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


Problem

给定一个无向连通图,其节点编号为 1 1 1 n n n,其边的权值为非负整数。试求出一条从 1 1 1 号节点到 n n n 号节点的路径,使得该路径上经过的边的权值的 “ “ XOR 和 ” ” 最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算 “ “ XOR 和 ” ” 时也要被重复计算相应多的次数。

直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 1 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 n n n 号节点为止,便得到一条从 1 1 1 号节点到 n n n 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的 “ “ XOR 和 ” ” 也不一样。现在请你求出该算法得到的路径的 “ “ XOR 和 ” ” 的期望值。

注:但是图中可能有重边或自环。

100 % 100\% 100% 的数据满足 2 ≤ m ≤ 100 2≤m≤100 2m100 m ≤ 10000 m≤10000 m10000


Solution

看到这种位运算的题,立即想到可以按位处理。

f [ u ] f[u] f[u] 表示 u → n u\rightarrow n un 的路径上异或和为 1 1 1概率,那显然 1 − f [ u ] 1-f[u] 1f[u] 就是这条路径上异或和为 0 0 0 的概率。

d e g [ u ] deg[u] deg[u] 表示 u u u 的度数,那么就有下面的式子:

f [ u ] = 1 d e g [ u ] ( ∑ w ( u , v ) = 0 f [ v ] + ∑ w ( u , v ) = 1 ( 1 − f [ v ] ) ) f[u]=\frac{1}{deg[u]}(\sum_{w(u,v)=0}f[v]+\sum_{w(u,v)=1}(1-f[v])) f[u]=deg[u]1(w(u,v)=0f[v]+w(u,v)=1(1f[v]))

一开始想到拓扑+ d p dp dp,但是图不是 D A G DAG DAG,有后效性,不能 d p dp dp

但是别急,把上面的式子再加以化简得:

d e g [ u ] f [ u ] − ∑ w ( u , v ) = 0 f [ v ] + ∑ w ( u , v ) = 1 f [ v ] = ∑ w ( u , v ) = 1 1 deg[u]f[u]-\sum_{w(u,v)=0}f[v]+\sum_{w(u,v)=1}f[v]=\sum_{w(u,v)=1}1 deg[u]f[u]w(u,v)=0f[v]+w(u,v)=1f[v]=w(u,v)=11

我们一共可以列出 n n n 个形如上式的式子,用高斯消元求解即可。

解出了 f [ u ] f[u] f[u],那当前的期望就是 1 × f [ u ] + 0 × ( 1 − f [ u ] ) = f [ u ] 1\times f[u]+0\times(1-f[u])=f[u] 1×f[u]+0×(1f[u])=f[u]

所以最后答案 a n s = ∑ i 2 i f i [ 1 ] ans=\sum_{i}2^if_i[1] ans=i2ifi[1]


Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 105
#define M 20005
using namespace std;
int n,m,t,deg[N];
int first[N],v[M],w[M],nxt[M];
double a[N][N],X[N];
void add(int x,int y,int z){
	nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
void Guass(){
	for(int i=1;i<=n;++i){
		int k=i;
		for(int j=i+1;j<=n;++j)
			if(fabs(a[k][i])<fabs(a[j][i]))
				k=j;
		swap(a[i],a[k]);
		for(int j=i+1;j<=n;++j)
			for(int k=i+1;k<=n+1;++k)
				a[j][k]-=a[i][k]*a[j][i]/a[i][i];
	}
	for(int i=n;i>=1;--i){
		for(int j=i+1;j<=n;++j)
			a[i][n+1]-=a[i][j]*X[j];
		X[i]=a[i][n+1]/a[i][i];
	}
}
int main(){
	int x,y,z;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),deg[x]++;
		if(x!=y)  add(y,x,z),deg[y]++;
	}
	double ans=0;
	for(int k=0;k<31;++k){
		memset(a,0,sizeof(a));
		for(int i=1;i<n;++i){
			a[i][i]=deg[i];
			for(int j=first[i];j;j=nxt[j]){
				int to=v[j],val=(w[j]>>k)&1;
				val?(a[i][to]++,a[i][n+1]++):(a[i][to]--);
			}
		}
		a[n][n]=1,Guass();
		ans+=X[1]*(1<<k);
	}
	printf("%.3lf",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值