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 2≤m≤100, m ≤ 10000 m≤10000 m≤10000。
Solution
看到这种位运算的题,立即想到可以按位处理。
设 f [ u ] f[u] f[u] 表示 u → n u\rightarrow n u→n 的路径上异或和为 1 1 1 的概率,那显然 1 − f [ u ] 1-f[u] 1−f[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)=0∑f[v]+w(u,v)=1∑(1−f[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)=0∑f[v]+w(u,v)=1∑f[v]=w(u,v)=1∑1
我们一共可以列出 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×(1−f[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;
}