BZOJ2337:[HNOI2011]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
Solution
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #define N (100+10) 6 using namespace std; 7 8 struct node{int to,next,len;}edge[N*N*2]; 9 double f[N][N],ans[N],Ans; 10 int n,m,u,v,l,Ind[N]; 11 int head[N],num_edge; 12 13 void add(int u,int v,int l) 14 { 15 edge[++num_edge].to=v; 16 edge[num_edge].next=head[u]; 17 edge[num_edge].len=l; 18 head[u]=num_edge; 19 } 20 21 void Gauss() 22 { 23 for (int i=1; i<=n; ++i) 24 { 25 int num=i; 26 for (int j=i+1; j<=n; ++j) 27 if (fabs(f[j][i])>fabs(f[num][i])) num=j; 28 if (num!=i) swap(f[i],f[num]); 29 for (int j=i+1; j<=n; ++j) 30 { 31 double t=f[j][i]/f[i][i]; 32 for (int k=i; k<=n+1; ++k) 33 f[j][k]-=t*f[i][k]; 34 } 35 } 36 for (int i=n; i>=1; --i) 37 { 38 for (int j=i+1; j<=n; ++j) 39 f[i][n+1]-=f[i][j]*ans[j]; 40 ans[i]=f[i][n+1]/f[i][i]; 41 } 42 } 43 44 int main() 45 { 46 scanf("%d%d",&n,&m); 47 for (int i=1; i<=m; ++i) 48 { 49 scanf("%d%d%d",&u,&v,&l); 50 add(u,v,l); Ind[u]++; 51 if (u==v) continue; 52 add(v,u,l); Ind[v]++; 53 } 54 for (int k=0; k<=30; ++k) 55 { 56 memset(ans,0,sizeof(ans)); 57 memset(f,0,sizeof(f)); 58 for (int i=1; i<n; ++i) 59 { 60 f[i][i]=1; 61 for (int j=head[i]; j; j=edge[j].next) 62 if ((edge[j].len>>k)&1) 63 { 64 f[i][edge[j].to]+=(double)1/Ind[i]; 65 f[i][n+1]+=(double)1/Ind[i]; 66 } 67 else f[i][edge[j].to]-=(double)1/Ind[i]; 68 } 69 for (int i=1; i<=n-1; ++i) f[n][i]=0; 70 f[n][n]=1;//钦定结果为0 71 Gauss(); 72 Ans+=ans[1]*(1<<k); 73 } 74 printf("%.3lf\n",Ans); 75 }