[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 }

 

posted @ 2017-10-22 12:13  Z-Y-Y-S  阅读(313)  评论(0编辑  收藏  举报