BZOJ 3143 游走 | 数学期望 高斯消元

啊 我永远喜欢期望题

BZOJ 3143 游走

题意

有一个n个点m条边的无向联通图,每条边按1~m编号,从1号点出发,每次随机选择与当前点相连的一条边,走到这条边的另一个端点,一旦走到n号节点就停下。每经过一条边,要付出这条边的编号这么多的代价。现将所有边用1~m重新编号,使总代价的期望最小,求这个最小值。

题解

我们可以求出每条边的期望经过次数,然后贪心地让经过次数多的边编号小即可。

直接用边来列方程求经过次数似乎列不出来,我们借助点来列方程。

设x[u]为从某个点出发的次数的期望,v为与u相连的点,d[v]为点d的度,则:
\[x[u] = \sum \frac{x[v]}{d[v]}\]

特殊地,不能从点n出发,所以x[n] = 0;第一次从点1出发,\(x[u] = 1 + \sum \frac{x[v]}{d[v]}\)。

解出所有x后,设一条边的两个端点是u和v,则经过每条边的次数的期望是:
\[\frac{x[u]}{d[u]} + \frac{x[v]}{d[v]}\]

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 505, M = 250005;
int n, m, u[M], v[M], d[N];
double ans[M], x[N], f[N][N], res;
void build(){
    for(int i = 1; i <= n; i++)
    f[i][i] = -1;
    for(int e = 1; e <= m; e++){
    f[u[e]][v[e]] += 1.0 / d[v[e]];
    f[v[e]][u[e]] += 1.0 / d[u[e]];
    }
    for(int i = 1; i <= n; i++)
    f[n][i] = 0;
    f[n][n] = 1, f[n][n + 1] = 0;
    f[1][n + 1] = -1;
}
void Gauss(){
    for(int i = 1; i <= n; i++){
    int l = i;
    for(int j = i + 1; j <= n; j++)
        if(fabs(f[j][i]) > fabs(f[l][i])) l = j;
    if(l != i)
        for(int j = i; j <= n + 1; j++)
        swap(f[i][j], f[l][j]);
    for(int j = n + 1; j >= i; j--)
        f[i][j] /= f[i][i];
    for(int j = i + 1; j <= n; j++)
        for(int k = n + 1; k >= i; k--)
        f[j][k] -= f[j][i] * f[i][k];
    }
    for(int i = n; i; i--){
    x[i] = f[i][n + 1];
    for(int j = 1; j < i; j++)
        f[j][n + 1] -= f[j][i] * x[i];
    }
}
int main(){
    read(n), read(m);
    for(int i = 1; i <= m; i++)
    read(u[i]), read(v[i]), d[u[i]]++, d[v[i]]++;
    build();
    Gauss();
    for(int i = 1; i <= m; i++)
    ans[i] = x[u[i]] / d[u[i]] + x[v[i]] / d[v[i]];
    sort(ans + 1, ans + m + 1);
    for(int i = 1; i <= m; i++)
    res += ans[i] * (m - i + 1);
    printf("%.3lf\n", res);
    return 0;
}
时间: 2024-08-01 04:54:09

BZOJ 3143 游走 | 数学期望 高斯消元的相关文章

【BZOJ】3143: [Hnoi2013]游走 期望+高斯消元

[题意]给定n个点m条边的无向连通图,每条路径的代价是其编号大小,每个点等概率往周围走,要求给所有边编号,使得从1到n的期望总分最小(求该总分).n<=500. [算法]期望+高斯消元 [题解]显然,应使经过次数越多的边编号越小,问题转化为求每条边的期望经过次数. 边数太多,容易知道f(u,v)=f(u)/out(u)+f(v)/out(v),所以转化为求每个点的期望经过次数,这就是驱逐猪猡了. 设f[x]表示点x的期望经过次数,根据全期望公式(讨论"经过"的问题不能依赖于下一步

【BZOJ 3143】【Hnoi2013】游走 期望+高斯消元

如果纯模拟,就会死循环,而随着循环每个点的期望会逼近一个值,高斯消元就通过列方正组求出这个值. #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const double eps=1e-9; bool vis[503]; double f[503],a[503][503],ans[500*500]; int N,M,cnt=

BZOJ 3105: [cqoi2013]新Nim游戏 [高斯消元XOR 线性基]

以后我也要用传送门! 题意:一些数,选择一个权值最大的异或和不为0的集合 终于有点明白线性基是什么了...等会再整理 求一个权值最大的线性无关子集 线性无关子集满足拟阵的性质,贪心选择权值最大的,用高斯消元判断是否和已选择的线性相关 每一位记录pivot[i]为i用到的行 枚举要加入的数字的每一个二进制为1的位,如果有pivot[i]那么就异或一下(消元),否则pivot[i]=这个数并退出 如果最后异或成0了就说明线性相关... #include <iostream> #include &l

BZOJ 2466: [中山市选2009]树( 高斯消元 )

高斯消元解异或方程组...然后对自由元进行暴搜.树形dp应该也是可以的... -------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> using namespace std; const int ma

BZOJ 1770: [Usaco2009 Nov]lights 燈( 高斯消元 )

高斯消元解xor方程组...暴搜自由元+最优性剪枝 ----------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> using namespace std; const int maxn = 49; int N, Id[max

BZOJ 2466 中山市选2009 树 高斯消元+暴力

题目大意:树上拉灯游戏 高斯消元解异或方程组,对于全部的自由元暴力2^n枚举状态,代入计算 这做法真是一点也不优雅... #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 110 using namespace std; int n,m; int f[M][M],is_free[M],tot; int ans[M],cnt; void

BZOJ 3569 DZY Loves Chinese II 高斯消元

题目大意:给定一个[魞歄连通图],多次询问当图中某k条边消失时这个图是否联通 强制在线 我们找到这个图的任意一棵生成树 然后对于每条非树边将其的权值赋为一个随机数 对于每条树边 我们将这条树边的权值设为所有覆盖这条树边的边权的异或和 那么图不连通当且仅当删除一条树边和覆盖这条树边的所有边集 而由于刚才的处理一条树边和覆盖这条边的所有边集的异或和为零 于是问题转化成了对于给定的k条边是否存在一个边权的异或和为零的子集 果断高斯消元 由于使用了随机化所以碰撞率极低 好方法学习了...构思真是巧妙 记

BZOJ.1923.[SDOI2010]外星千足虫(高斯消元 异或方程组 bitset)

题目链接 m个方程,n个未知量,求解异或方程组. 复杂度比较高,需要借助bitset压位. 感觉自己以前写的(异或)高斯消元是假的..而且黄学长的写法都不需要回代. //1100kb 324ms #include <cstdio> #include <cctype> #include <bitset> #include <algorithm> const int N=1004,M=2004; int n,m; char s[N]; std::bitset&l

bzoj 1923 [Sdoi2010]外星千足虫 高斯消元

题面 题目传送门 解法 学习了怎么用高斯消元解一个异或方程组 其实和普通的高斯消元是一样的 在多少个方程后就确定答案可以直接边做边取max即可 用bitset优化异或 时间复杂度:\(O(\frac{nm^2}{w})\) 代码 #include <bits/stdc++.h> using namespace std; template <typename node> void chkmax(node &x, node y) {x = max(x, y);} templat