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);
}