第一题
【题目描述】
有一个点数为n,边数为m的无向连通图,点编号1~n,边有权值。现在随机选择一条从点1出发到点n的路径(按照每次的出边等概率随机的方式,第一次到达n后即结束),将路径上所有边的权值和进行异或作为路径的权值,求此图路径权值的期望。图可能有重边和自环。自环为单项边。
【输入】
输入第一行包含两个空格隔开的正整数n,m,之后m行每行三个整数,表示一条边的起点终点和权值。
【输出】
输出一个实数,保留到三位小数表示期望。
【输入样例】
2 2
1 1 1
1 2 0
【输出样例】
0.333
【提示】
走自环奇数次则为1,偶数次则为0.期望为无穷等比级数之和1/4+1/16+1/64+……=1/3.
对于20%的数据点,n=2。
对于40%的数据点,2 ≤ n ≤ 10。
对于第5、6个数据点,权值只为0或1.
对于100%的数据点,2 ≤ n ≤ 100,0 ≤ 权值 ≤ 32767,1 ≤ m ≤ 1000。
先看这题,然后再看这题会不会有什么感想?
如何处理异或呢?说到位运算就想到拆位。拆了拆了。
然后发现每个点只能是0/1,然后稍加思索,冥思苦想,空气力学分析可得:
每个点分为0和1两个状态,然后根据边的值来在4个状态之间连边。
那么每一条边的贡献就是 (概率(0->1) - 概率(1->0)) * 边权。
一共搞15次就好了。
1 #include <cstdio> 2 #include <bitset> 3 #include <algorithm> 4 #include <cmath> 5 const int N = 205, M = 1010, lm = 15; 6 const double eps = 1e-12; 7 8 int n, l[M], r[M], c[M], out[N]; 9 double a[N][N], f[N]; 10 11 inline void clear() { 12 for(int i = 1; i <= (n << 1); i++) { 13 for(int j = 1; j <= (n << 1 | 1); j++) { 14 a[i][j] = 0.0; 15 } 16 f[i] = 0.0; 17 } 18 return; 19 } 20 21 inline void output() { 22 for(int i = 1; i <= (n << 1); i++) { 23 for(int j = 1; j <= (n << 1 | 1); j++) { 24 printf("%6.3lf ", a[i][j]); 25 } 26 puts(""); 27 } 28 puts(""); 29 return; 30 } 31 32 inline void Gauss() { 33 for(int i = 1; i < (n << 1); i++) { 34 for(int j = i; j <= (n << 1); j++) { 35 if(fabs(a[j][i]) > eps) { 36 std::swap(a[i], a[j]); 37 break; 38 } 39 } 40 for(int j = i + 1; j <= (n << 1); j++) { 41 if(fabs(a[j][i]) < eps) { 42 continue; 43 } 44 double p = a[j][i] / a[i][i]; 45 for(int k = i; k <= (n << 1 | 1); k++) { 46 a[j][k] -= a[i][k] * p; 47 } 48 } 49 } 50 51 for(int i = (n << 1); i > 1; i--) { 52 for(int j = i - 1; j >= 1; j--) { 53 if(fabs(a[j][i]) < eps) { 54 continue; 55 } 56 double p = a[j][i] / a[i][i]; 57 a[j][i] = 0.0; 58 a[j][n << 1 | 1] -= p * a[i][n << 1 | 1]; 59 } 60 } 61 62 for(int i = 1; i <= (n << 1); i++) { 63 f[i] = a[i][n << 1 | 1] / a[i][i]; 64 } 65 return; 66 } 67 68 int main() { 69 int m; 70 scanf("%d%d", &n, &m); 71 72 for(int i = 1; i <= m; i++) { 73 scanf("%d%d%d", &l[i], &r[i], &c[i]); 74 out[l[i]]++; 75 if(l[i] != r[i]) { 76 out[r[i]]++; 77 } 78 } 79 80 double ans = 0.0; 81 82 for(int i = 0; i < lm; i++) { 83 if(i) { 84 clear(); 85 } 86 for(int j = 1; j <= m; j++) { 87 int x = l[j], y = r[j], t = (c[j] >> i) & 1; 88 double ox = 1.0 / out[x], oy = 1.0 / out[y]; 89 if(x == y) { 90 if(x == n) { 91 continue; 92 } 93 if(t) { 94 a[x][x + n] += ox; 95 a[x + n][x] += ox; 96 } 97 else { 98 a[x][x] += ox; 99 a[x + n][x + n] += ox; 100 } 101 continue; 102 } 103 if(x < n) { 104 if(t) { 105 a[y][x + n] += ox; 106 a[y + n][x] += ox; 107 } 108 else { 109 a[y][x] += ox; 110 a[y + n][x + n] += ox; 111 } 112 } 113 if(y < n) { 114 if(t) { 115 a[x][y + n] += oy; 116 a[x + n][y] += oy; 117 } 118 else { 119 a[x][y] += oy; 120 a[x + n][y + n] += oy; 121 } 122 } 123 } 124 for(int j = 1; j <= (n << 1); j++) { 125 a[j][j] -= 1.0; 126 } 127 a[1][n << 1 | 1] = -1.0; 128 129 Gauss(); 130 131 int base = 1 << i; 132 for(int j = 1; j <= m; j++) { 133 if(((c[j] >> i) & 1) == 0) { 134 continue; 135 } 136 int x = l[j], y = r[j]; 137 double ox = 1.0 / out[x]; 138 double oy = 1.0 / out[y]; 139 140 if(x == y) { 141 if(x == n) { 142 continue; 143 } 144 ans += (f[x] - f[x + n]) * ox * base; 145 continue; 146 } 147 if(x < n) { 148 ans += (f[x] - f[x + n]) * ox * base; 149 } 150 if(y < n) { 151 ans += (f[y] - f[y + n]) * oy * base; 152 } 153 } 154 } 155 156 printf("%.3lf", ans); 157 158 return 0; 159 }