http://www.lydsy.com/JudgeOnline/problem.php?id=2337
题目
有个无向图,从1走到N可以得到边权的序列,将这些权异或起来,可以得到一个值。现在你从1开始,每次等概率随机地选择一条边走向下一个点,一直走到N。问在N得到的异或的值的期望是多少。
边权<=$10^9$
N<=100,M<=10000,可能有重边和自环。
题解
xor就只能处理每一位了,如果边权上这一位是1,说明是1的概率会反过来变成是0的概率,因此概率变为(1-x)
设dp[i][k]为从i出发到N时,第K位是1的概率
因为期望有线性性,所以可以拆成31位计算。
方程是
[dp[i][k]=frac{1}{n}sum dp[j][k]]
[dp[n][k]=0]
n表示i点上面的边数,如果是自环,边数只能+1
由于题目提示了精度,为了减小精度误差,应该尽量少用除法
AC代码
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) using namespace std; typedef long long ll; int hd[107], nxt[20007], to[20007], vv[20007], deg[107], en; double dp[107][31]; double arr[107][107][31]; inline void adde(int a, int b, int v) { nxt[en]=hd[a]; hd[a]=en; to[en]=b; vv[en]=v; en++; } int n,m; inline void calc() { REP(k,0,31) { REPE(i,1,n) { int ch=i; double ma=fabs(arr[i][i][k]); REPE(j,i+1,n) { if(fabs(arr[j][i][k])>ma) ch=j, ma=fabs(arr[j][i][k]); } if(ch!=i) REPE(j,0,n) swap(arr[i][j][k], arr[ch][j][k]); REPE(j,i+1,n) { double ra=arr[j][i][k]/arr[i][i][k]; REPE(x,i+1,n) arr[j][x][k]-=arr[i][x][k]*ra; arr[j][0][k]-=arr[i][0][k]*ra; } } PERE(i,n,1) { REPE(j,i+1,n) arr[i][0][k]-=arr[i][j][k]*arr[j][0][k]; arr[i][0][k]/=arr[i][i][k]; } } } int main() { memset(hd,-1,sizeof hd); scanf("%d%d", &n, &m); en=0; memset(deg,0,sizeof deg); REP(i,0,m) { int u,v,w; scanf("%d%d%d", &u, &v, &w); if(u!=v) adde(u,v,w), deg[v]++; adde(v,u,w); deg[u]++; } memset(dp[n],0,sizeof(dp[n])); memset(arr,0,sizeof arr); REPE(i,1,n) REP(k,0,31) arr[i][i][k]=-1; REP(i,1,n) { double d=1.0/deg[i]; for(int p=hd[i]; ~p; p=nxt[p]) { int j=to[p]; REP(k,0,31) { int f=1<<k; if(vv[p]&f) { arr[i][j][k]-=d; arr[i][0][k]-=d; } else { arr[i][j][k]+=d; } } } } calc(); double ans=0; REP(k,0,31) { ans+=(1<<k)*arr[1][0][k]; } printf("%.3f ", ans); }