题目链接:戳我
对于异或,有一个套路是要把每一位拆开算贡献,这个题就是这样子的。
对于当前位,我们设\(dp[i]\)表示从i到n的路径上该位为1的概率。
(为什么不设\(dp[i]\)表示从1到i的路径上的概率呢,因为有可能当前点到达不了n)
\[dp[u]=\sum_{w(u,v)=1}\frac{1-dp[v]}{du[u]}+\sum_{w(u,v)=0}\frac{dp[v]}{du[u]}\]
然后因为这个不能线性递推,所以我们设未知数,用高斯消元解方程就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 101
using namespace std;
int n,m,t;
int du[MAXN+10],head[MAXN+10];
double ans;
double f[MAXN+10][MAXN+10],sum[MAXN+10];
struct Edge{int nxt,to,dis;}edge[200010];
inline void add(int from,int to,int dis){edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;}
inline void init(int pos)
{
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++) f[i][i]=1.0;
for(int i=1;i<n;i++)
for(int j=head[i];j;j=edge[j].nxt)
{
int v=edge[j].to;
if(edge[j].dis&(1<<pos)) f[i][v]+=1.0/du[i],f[i][n+1]+=1.0/du[i];
else f[i][v]-=1.0/du[i];
}
}
inline void guass()
{
for(int i=1;i<=n;i++)
{
int pos=i;
for(int j=i+1;j<=n;j++)
if(fabs(f[j][i])>fabs(f[pos][i]))
pos=j;
if(pos!=i) swap(f[pos],f[i]);
double cur=f[i][i];
for(int j=i+1;j<=n+1;j++) f[i][j]/=cur;
for(int j=i+1;j<=n;j++)
{
cur=f[j][i];
for(int k=i+1;k<=n+1;k++)
f[j][k]-=f[i][k]*cur;
}
}
sum[n]=f[n][n+1];
for(int i=n-1;i>=1;i--)
{
sum[i]=f[i][n+1];
for(int j=i+1;j<=n;j++) sum[i]-=f[i][j]*sum[j];
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w),du[u]++;
if(u!=v) add(v,u,w),du[v]++;
}
for(int i=0;i<=31;i++)
{
init(i);
guass();
ans+=1.0*(1ll<<i)*sum[1];
}
printf("%.3lf\n",ans);
return 0;
}