[HNOI2011]XOR和路径 概率期望 高斯消元

时间:2021-07-11 02:32:08

题面

题解:因为异或不太好处理,,,因此按位来算,这样最后的答案就是每一位上的值乘对应的权值再求和。本着期望要倒退的原则,,,我们设$f[i]$表示从$i$到$n$,xor和为1的概率。
那么观察$xor$的规则:
1 xor 1 = 0
0 xor 1 = 1 ----> 当xor 1时,结果为1的概率 = 原本为0的概率
1 xor 0 = 1
0 xor 0 = 0 ----> 当xor 0时,结果为1的概率 = 原本为1的概率
因此我们有如下转移:
$$f[x] = \frac{1}{d_{x}}(\sum_{val = 0, (x, y) \in E} f[y] + \sum_{val = 1, (x, y) \in E} (1 - f[y]))$$
$$d_{x} f[x] = \sum_{val = 0, (x, y) \in E} f[y] + \sum_{val = 1, (x, y) \in E} (1 - f[y])$$
$$d_{x} f[x] - \sum_{val = 0, (x, y) \in E} f[y] + \sum_{val = 1, (x, y) \in E} f[y] = \sum_{val = 1, (x, y) \in E} 1$$
于是我们可以发现我们一共有n个方程,n个元,于是高斯消元即可。注意$f[n] = 0$

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 110
#define ac 31000
#define LL long long const double eps = 1e-;
int n, m, maxn;
double ans;
int d[AC], id[AC];
int Head[AC], Next[ac], date[ac], len[ac], tot;
double f[AC][AC]; inline int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline void add(int f, int w, int S)
{
date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S, ++ d[f];
if(f != w) date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot, len[tot] = S, ++ d[w];
}//如果是自环的话只能算一次 inline void upmax(int &a, int b){
if(b > a) a = b;
} void pre()
{
n = read(), m = read(), id[] = ;
for(R i = ; i <= m; i ++)
{
int a = read(), b = read(), c = read();
add(a, b, c), upmax(maxn, c);
}
for(R i = ; i <= ; i ++) id[i] = id[i - ] << ;
} void check()
{
printf("\n");
for(int i = ; i <= n; i ++)
{
for(int j = ; j <= n + ; j ++) printf("%.2lf ", f[i][j]);
printf("\n");
}
} void gauss()
{
//check();
for(R i = , r = ; i <= n; i ++, r = i)
{
for(R j = i; j <= n; j ++)
if(fabs(f[j][i]) > eps) {r = j; break;}
if(fabs(f[r][i]) < eps) return ;
if(i != r) for(R j = ; j <= n + ; j ++) swap(f[i][j], f[r][j]);
for(R j = i + ; j <= n + ; j ++) f[i][j] /= f[i][i];
for(R j = ; j <= n; j ++)
{
if(i == j) continue;//是continue不是break啊。。。。
for(R k = i + ; k <= n + ; k ++) f[j][k] -= f[i][k] * f[j][i];
}
}
} void build(int lim)
{
memset(f, , sizeof(f));//先清空
for(R i = ; i < n; i ++)
{
f[i][i] = d[i];
for(R j = Head[i]; j; j = Next[j])
{
int now = date[j];
if(id[lim] & len[j]) ++ f[i][now], ++ f[i][n + ];
else -- f[i][now];//不能直接赋值,因为可能会涉及到自己,,于是本来就有系数,就是抵消一部分而不是覆盖全部了
}
}
//for(R i = 1; i <= n + 1; i ++) f[n][i] = (i == n);//特判最后一个点
f[n][n] = ;
} void work()
{
LL tmp = ;
for(R i = ; id[i] <= maxn; i ++)
{
build(i), gauss();
ans += f[][n + ] * tmp, tmp <<= ;
// printf("!!!%.3lf\n", ans);
}
printf("%.3lf\n", ans);
} int main()
{
// freopen("in.in", "r", stdin);
pre();
work();
// fclose(stdin);
return ;
}