【洛谷】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;
}