[HNOI 2011]XOR和路径
Description
给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数。试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。
直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 N 号节点为止,便得到一条从 1 号节点到 N 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR 和”的期望值。
Input
从文件input.txt中读入数据,输入文件的第一行是用空格隔开的两个正整数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,但是图中可能有重边或自环。
Output
输出文件 output.txt 仅包含一个实数,表示上述算法得到的路径的“XOR 和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算)
Sample Input
2 2 1 1 2 1 2 3
Sample Output
2.333
HINT
样例解释:有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。
转载自Navi_Awson巨佬%%%%%oTTTTTTTTTZ
http://www.cnblogs.com/NaVi-Awson/p/7707368.html
首先看到路径$xor$值,还是选择按位做。
我们设$f_u$表示从$u$到$n$的路径异或值为$1$的概率。显然$f_n == 0$。
此外,设$w(u, v)$为$u->v$的边权($1/0$),那么有:
$$f_u = \sum_{(u,v) \in E,\ w(u,v) = 0} \frac{f_v}{degree_u} + \sum_{(u,v) \in E,\ w(u,v) = 1} \frac{1-f_v}{degree_u}$$
那么我们可以得到$n$个方程,用高斯消元求解。
可以乘上$degree_u$减小误差。
这题特殊说明一下为什么不能顺推而要逆推:
很多题解的说法是因为“如果正推的话,$1−f_i$代表的不仅从$1$到$i$异或和不为$1$的概率,还包含了从$1$不走到$i$的概率,无法转移”。
如果这样解释,那就解释不了$i$走不到$n$的情况。
我认为合理的解答是:因为$1$可以重复走多次,而$n$只能走$1$次。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 struct Node 8 { 9 int next,to; 10 int dis; 11 }edge[20005]; 12 double a[105][105],ans; 13 int n,m,head[105],num,d[105]; 14 int pw[31]; 15 void add(int u,int v,int dis) 16 { 17 num++; 18 edge[num].next=head[u]; 19 head[u]=num; 20 edge[num].to=v; 21 edge[num].dis=dis; 22 } 23 void gauss() 24 {int i,j,now,k; 25 for (i=1;i<=n;i++) 26 { 27 now=i; 28 for (j=i+1;j<=n;j++) 29 if (fabs(a[now][i])<fabs(a[j][i])) 30 now=j; 31 for (j=i;j<=n+1;j++) 32 swap(a[now][j],a[i][j]); 33 for (j=i+1;j<=n+1;j++) 34 a[i][j]/=a[i][i]; 35 a[i][i]=1; 36 for (j=i+1;j<=n;j++) 37 { 38 for (k=i+1;k<=n+1;k++) 39 a[j][k]-=a[i][k]*a[j][i]; 40 a[j][i]=0; 41 } 42 } 43 for (i=n;i>=1;i--) 44 { 45 for (j=i+1;j<=n;j++) 46 { 47 a[i][n+1]-=a[i][j]*a[j][n+1]; 48 a[i][j]=0; 49 } 50 a[i][n+1]/=a[i][i]; 51 a[i][i]=1; 52 } 53 } 54 int main() 55 {int i,j,u,v,w; 56 cin>>n>>m; 57 pw[0]=1; 58 for (i=1;i<=30;i++) 59 pw[i]=pw[i-1]*2; 60 for (i=1;i<=m;i++) 61 { 62 scanf("%d%d%d",&u,&v,&w); 63 add(u,v,w); 64 d[v]++; 65 if (u!=v) add(v,u,w),d[u]++; 66 } 67 for (i=0;i<=30;i++) 68 { 69 memset(a,0,sizeof(a)); 70 for (u=1;u<n;u++) 71 { 72 a[u][u]=d[u]; 73 for (j=head[u];j;j=edge[j].next) 74 { 75 int v=edge[j].to; 76 if (edge[j].dis&pw[i]) a[u][v]++,a[u][n+1]++; 77 else a[u][v]--; 78 } 79 } 80 a[n][n]=1; 81 gauss(); 82 ans+=a[1][n+1]*(double)pw[i]; 83 } 84 printf("%.3lf\n",ans); 85 }