CF1657E Star MST

发布时间 2023-05-09 16:40:20作者: ereoth

Problem

有一个 \(n\) 个点的无向完全图,边权 $ e∈[1,m]$ ,已知该图的最小生成树的权值与所有与 \(1\) 号点相连的边的边权和相同,求有多少种构图方式,答案对 \(998244353\) 取模。

\(2\leq n \leq 250 , 1 \leq m \leq 250\)

Input

一行两个整数 \(n\)\(k\)

Output

一行一个整数表示答案。

Sample

Input 1

3 2

Output 1

5

Input 2

4 4

Output 2

571

Input 3

6 9

Output 3

310640163

Input 4

42 13

Output 4

136246935

Solution

比较难想的DP题。

考虑定义 \(f_{i,j}\) 表示当前给与 \(1\) 相连的 \(j\) 条边分配了权值,每条边权值在 \([1,i]\) 之间的方案数。

由题意可以得到,对于一条边 \((x, y)\),若 \(x \neq 1\)\(y \neq 1\),则该边的权值 \(W_{x,y} \geq W_{1, x}\)\(W_{x,y} \geq W_{1, y}\)

\(n,k\) 都很小,可以考虑两层循环枚举状态,一层循环进行转移。

假设我们已知为 \(f_{i - 1,j}\) 的答案,从剩下的 \(n-1-j\) 条边中选出 \(k\) 条边赋值为 \(i\),容易可以得到转移:

\(f_{i,j+k} = f_{i,j+k}+ f_{i-1,j} * c_{n-j-1,k} * Pow(m-i+1,j*k+k*(k-1)/2)\)

其中 \(c_{n-j-1,k}\) 为从 \(n-j-1\) 条边中选出 \(k\) 条边的方案数。(组合数)

加入的这 \(k\) 条边之间连出的 \(k*(k-1)/2\) 条边以及这 \(k\) 条边与前面 \(j\) 条边之间连出的 \(j\times k\) 条边的权值都必须在 \([i,m]\) 之间,每一条边都有 \(m-i+1\) 种选择,还需乘上这部分的贡献。

最后 \(f_{m,n-1}\) 即为答案,权值不超过 \(i\),分配了 \(n-1\) 条边。

代码:

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int kmax = 255;
const int Mod = 998244353;

int n, m;
long long c[kmax][kmax];
long long f[kmax][kmax];

long long Pow(long long x, long long y) {
  long long r = 1;
  for (; y; y >>= 1) {
    if (y & 1) r = r * x % Mod;
    x = x * x % Mod;
  }
  return r;
}

void Init() { // 预处理组合数
  c[0][0] = 1;
  for (int i = 1; i < kmax; i++) {
    c[i][0] = 1;
    for (int j = 1; j <= i; j++) {
      c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % Mod;
    }
  }
}

int main() {
  Init();
  cin >> n >> m;
  f[0][0] = 1;
  for (int i = 1; i <= m; i++) {
    for (int j = 0; j < n; j++) {
      for (int k = 0; k < n - j; k++) {
        f[i][j + k] = (f[i][j + k] + f[i - 1][j] * c[n - j - 1][k] % Mod * Pow(m - i + 1, j * k + k * (k - 1) / 2) % Mod) % Mod; // 直接转移,记得取模
      }
    }
  }
  cout << f[m][n - 1];
  return 0;
}