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 }
posted @ 2018-04-28 10:34  Refun  阅读(264)  评论(0编辑  收藏  举报