xor和路径(codevs 2412)
给定一个无向连通图,其节点编号为1到N,其边的权值为非负整数。试求出一条从1号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。
直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从1号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到N号节点为止,便得到一条从1号节点到N号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR和”的期望值。
第一行是用空格隔开的两个正整数N和M,分别表示该图的节点数和边数。紧接着的M行,每行是用空格隔开的三个非负整数u,v和w(1≤u,v≤N, 0≤w≤109),表示该图的一条边(u,v),其权值为w。输入的数据保证图连通,30%的数据满足N≤30,100%的数据满足2≤N≤100,M≤10000,但是图中可能有重边或自环。
仅包含一个实数,表示上述算法得到的路径的“XOR和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算)
2 2
1 1 2
1 2 3
2.333
样例解释:有1/2的概率直接从1号节点走到2号节点,该路径的“XOR和”为3;有1/4的概率从1号节点走一次1号节点的自环后走到2号节点,该路径的“XOR和”为1;有1/8的概率从1号节点走两次1号节点的自环后走到2号节点,该路径的“XOR和”为3;......;依此类推,可知“XOR和”的期望值为:3/2+1/4+3/8+1/16+3/32+....=7/3,约等于2.333。
数据范围如题
/* 对于异或的题目,一般是按位拆分,设f[x]为从x到n的异或期望。 f[x]=Σ(1/d[x])*f[y](边权为0)+Σ(1/d[x])*(1-f[y])(边权为1) 将上式变形得到: f[x]-Σ(1/d[x])*f[y](边权为0)+Σ(1/d[x])*f[y](边权为1)=Σ (1/d[x])(边权为1) 然后高斯消元。 */ #include<cstdio> #include<iostream> #include<cstring> #include<cmath> #define N 110 #define M 10010 #define ld long double using namespace std; int head[N],d[N],cnt,n,m; ld a[N][N],ans; struct node{int v,w,pre;}e[M*2]; void add(int u,int v,int w){ e[++cnt].v=v;e[cnt].w=w;e[cnt].pre=head[u];head[u]=cnt; } void gauss(){ for(int i=1;i<=n;i++){ int id=i;ld maxn=fabs(a[i][i]); for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>maxn) id=j,fabs(a[j][i]); if(id!=i) swap(a[i],a[id]); ld t=a[i][i]; for(int j=1;j<=n+1;j++) a[i][j]/=t; for(int j=1;j<=n;j++) if(j!=i){ ld t=a[j][i]; for(int k=1;k<=n+1;k++) a[j][k]-=t*a[i][k]; } } } int main(){ 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);d[u]++; if(u!=v) add(v,u,w),d[v]++; } for(int t=0;t<=30;t++){ memset(a,0,sizeof(a)); for(int i=1;i<n;i++){ a[i][i]=1.0; for(int j=head[i];j;j=e[j].pre){ if(e[j].w&(1<<t)) a[i][e[j].v]+=1.0/d[i],a[i][n+1]+=1.0/d[i]; else a[i][e[j].v]-=1.0/d[i]; } } a[n][n]=1.0;gauss();ans+=a[1][n+1]*(1<<t); } printf("%.3lf",(double)ans); return 0; }