给定一个有向无环图,个点,条边,可以存在重边。设为图中所有简单路径(允许长度为,即起点与终点相同)的集合。
从集合中等概率随机选择一条路径并沿着它行走,定义一条路径的长度为其经过的边数
即随机变量为所选路径的长度,计算的期望值对取模的结果,即
显然是离散型随机变量:的取值可以为,设对应的路径条数为
那么
所以现在就是求每个长度的路径的条数:
因为每个点都可以作为起点,如果从每个点一遍的话,时间复杂度为,一定超时的!
所以用到了拓扑排序+的方法:
定义表示以节点为终点的全部路径条数;当时,
定义表示以节点为终点的全部路径的长度;当时,
详细解释:
  1. ,从新增加了一条边,这一条边可以和以为终点的所有路径构成新的路径,那么新增加了条路径
  2. ,从新增加了一条边,这一条边可以和以为终点的所有路径构成新的路径,原来条路径的长度为,现在每条路径新增加了长度,所以在的基础上增加了长度为,所以一共增加了
总代码:
#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;


const int N = 1e5 + 10;
const int mod = 998244353;

int n, m;
vector<int> adj[N];
int deg[N];

int f[N], g[N];

int ksm(int a, int b, int p){
    a %= p;
    int sum = 1;
    while(b){
        if(b & 1) sum = (sum * a) % mod;
        b >>= 1;
        a = (a * a) % mod;
    }
    return sum;
}

int gcd(int a, int b){
    if(!b) return a;
    else return gcd(b, a % b);
}

signed main(){
    HelloWorld;
    
    cin >> n >> m;
    for(int i = 1; i <= m; i ++){
        int u, v; cin >> u >> v;
        adj[u].push_back(v);
        deg[v] ++;
    }    
    for(int i = 1; i <= n; i ++) f[i] = 1;
    queue<int> q;
    for(int i = 1; i <= n; i ++){
        if(!deg[i]) q.push(i);
    }
    while(q.size()){
        int u = q.front();
        q.pop();
        for(int v : adj[u]){
            if(-- deg[v] == 0) q.push(v);
            f[v] = (f[v] + f[u]) % mod;
            g[v] = (g[v] + f[u] + g[u]) % mod;
        }
    }
    int fz = 0, fm = 0;
    for(int i = 0; i <= n; i ++){
        fz = (fz + g[i]) % mod;
        fm = (fm + f[i]) % mod;
    }
    int g = gcd(fz, fm);
    fz /= g, fm /= g;
    cout << (fz * ksm(fm, mod - 2, mod)) % mod;
    return 0;
}