Luogu P7474 学术精神

题目

对于每个点 $ i $ 随机与 $ [1,n] $ 中的一点连无向边,若连向自己,则保留该边并再次连边,一直重复至连到别的点上为止,求边数与连通块个数期望。

思路

来恶补一下概率期望

第一问

一个点连向其他点的概率为 \(\frac{n - 1}{n}\) ,所以期望 \(\frac{n}{n - 1}\) 次即可完成连边。

一共有 \(n\) 个点,所以第一问的答案就是 \(\frac{n \times n}{n - 1}\)

第二问

这里有一个结论就是在本题条件下,对于每一个确定的图,图的联通块数与其所含环的数量相等。那么我们只需要计算出在所有的图中总共有多少环,再除以图的数量,那么就是最后的答案了。

令环的总数为 \(sum\) ,图的总数为 \(num\)

则有

\[num=(n-1)^n \]

\[Sum=\sum_{i=2}^{n} \times C_{n}^{i} \times(i-1)! \times(n-1)^{n-i} \]

解释一下 \(sum\) 的含义,首先我们先确定环的大小 \(i\) ,然后从 \(n\)个点中选择 \(i\) 个成环,可以形成环的种类是 \((i-1)!\)(圆排列),其余点随便连边,最后对其求和(每个确定的环在多少图中出现过)。因为 \(\Sigma\) (所有的图中有多少环)与 \(\Sigma\) (每个确定的环在多少图中出现过)在数值上是等价的,所以可以进行如上计算。

code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10010
#define M 1010
#define ll long long

using namespace std;
const int mod = 998244353;
int n, fac[N];

int read() {
  int s = 0, f = 0; char ch = getchar();
  while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
  while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
  return f ? -s : s;
}

int q_pow(int a, int b) {
  int ans = 1;
  while (b) {
    if (b & 1) ans = (1ll * ans * a) % mod;
    a = (1ll * a * a) % mod;
    b >>= 1;
  }
  return ans;
}

int C(int n, int m) {
  return 1ll * fac[n] * q_pow(1ll * fac[m] * fac[n - m] % mod, mod - 2) % mod;
}

int main() {
  n = read();
  fac[1] = fac[0] = 1;
  for (int i = 2; i <= n; i++) fac[i] = (1ll * fac[i - 1] * i) % mod;
  cout << 1ll * n * n % mod * q_pow(n - 1, mod - 2) % mod << "\n";
  int sum = 0;
  for (int i = 2; i <= n; i++) 
    sum = (1ll * sum + 1ll * C(n, i) * fac[i - 1] % mod * q_pow(n - 1, n - i) % mod) % mod;
  sum = 1ll * sum * q_pow(q_pow(n - 1, n), mod - 2) % mod;
  cout << sum;
}
posted @ 2021-11-17 19:52  Kersen  阅读(58)  评论(5编辑  收藏  举报