Loading

P8859 冒泡排序

我回来了。

参考:https://www.luogu.com.cn/blog/_post/509374https://www.luogu.com.cn/blog/_post/510710

考虑 type 1,注意到 \(1\) 是不能被超越的,且一个数操作多次不优,因此第一步操作 \(1\) 不劣。因此从小到大归位每个数不劣,答案即为总数减去前缀 \(\max\) 的数目。从小到大插入并计数即可。

考虑对序列做所有轮换,最优解一定在这 \(n\) 种轮换断环成链后的答案中。于是对所有轮换找前缀 \(\max\)\(\max\) 即可。

用后继刻画轮换。将每个点向后面离它最近的大于它的数值连有向边,则图中最长路即为答案。这会形成以 \(n\) 为根的内向树。

这样转化极好。它提供了设计子结构的方法。同时,它是双射:树与序列的数目都是 \((n-1)!\),一棵树一定能转化为一个序列,一个序列一定能转化成一棵树。

树转序列,递归:

  • \(u\) 加入序列
  • 从小到大递归 \(u\) 的儿子

容易发现一定合法。

因此只需要对父亲编号大于儿子编号的树计数。

\(f_{i, j}\) 表示:\(i\) 点深度最大值为 \(j\) 的树的个数。转移时把一棵子树合并进当前树中:

\[f_{i+j, \max(d_1, d_2+1)} \gets \binom{i + j - 2}{i - 1} f_{i, d_1} f_{j, d_2} \]

在第二维度上记录前缀 \(\max\) 即可。

// start time : 
// debug time :
// end time : 
#include <bits/stdc++.h>
const int M = 505;

const int mod = 1e9 + 7;
struct modint {
  int x;
  modint(int o = 0) {x = o;}
  modint &operator = (int o) {return x = o, *this;}
  modint &operator += (modint o) {return x = x+o.x >= mod ? x+o.x-mod : x+o.x, *this;}
  modint &operator -= (modint o) {return x = x-o.x < 0 ? x-o.x+mod : x-o.x, *this;}
  modint &operator *= (modint o) {return x = 1ll*x*o.x%mod, *this;}
  modint &operator ^= (int b) {
    modint a = *this, c = 1;
    for (; b; b >>= 1, a *= a) if(b & 1) c *= a;
    return x=c.x, *this;
  }
  modint &operator /= (modint o) {return *this *= o ^= mod-2;}
  friend modint operator + (modint a, modint b) {return a += b;}
  friend modint operator - (modint a, modint b) {return a -= b;}
  friend modint operator * (modint a, modint b) {return a *= b;}
  friend modint operator / (modint a, modint b) {return a /= b;}
  friend modint operator ^(modint a,int b) {return a ^= b;}
  friend bool operator == (modint a, int b) {return a.x == b;}
  friend bool operator != (modint a, int b) {return a.x != b;}
  bool operator ! () {return !x;}
  modint operator - () {return x ? mod-x : 0;}
  bool operator < (const modint &b) const {return x < b.x;}
};
inline modint qpow(modint x, int y) {return x ^ y;}

std::vector<modint> fac, ifac, iv;

inline modint sign(int n) {return (n&1) ? (mod-1) : (1);}


modint solve1(int n, int m) {
  std::vector<std::vector<modint>> f(n + 1, std::vector<modint>(m + 1));
  f[0][0] = 1;
  for (int i = 1; i <= n; i++) {
    // f[i][0] = 1;
    for (int j = 1; j <= m; j++) {
      f[i][j] = f[i - 1][j - 1] + f[i - 1][j] * (i - 1);
    }
  }
  return f[n][m];
}

modint solve2(int n, int m) {
  std::vector<std::vector<modint>> f(n + 1, std::vector<modint>(n + 1));
  std::vector<std::vector<modint>> s(n + 1, std::vector<modint>(n + 1));
  std::vector<std::vector<modint>> C(n + 1, std::vector<modint>(n + 1));
  C[0][0] = 1;
  for (int i = 1; i <= n; i++) {
    C[i][0] = 1;
    for (int j = 1; j <= i; j++)
      C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
  }
  f[1][1] = 1;
  for (int i = 1; i <= n; i++)
    s[1][i] = 1;
  for (int t = 2; t <= n; t++) {
    // f[t][t] = 1;
    for (int i = 1; i < t; i++) {
      int j = t - i;
      modint c = C[i + j - 2][i - 1];
      for (int d = 1; d <= t; d++) {
        f[t][d] += c * f[i][d] * s[j][d - 1]; // d = d1 >= d2 + 1
        f[t][d] += c * s[i][d - 1] * f[j][d - 1]; // d = d2 + 1 > d1
      }
    }
    for (int i = 1; i <= n; i++)
      s[t][i] = s[t][i - 1] + f[t][i];
  }
  return f[n][m];
}

int main() {
  int n, m, t; scanf("%d %d %d", &n, &m, &t);
  if (t == 1) printf("%d\n", solve1(n, n - m));
  else printf("%d\n", solve2(n, n - m));
  return 0;
}
/*
stupid mistakes:
  - 初始化 f[1][1] = 1 而非 f[1][0]
  - 链的情况在 i=1, d=t 时考虑,不用特判
  - 前缀和取到 n
*/
posted @ 2023-07-27 12:35  purplevine  阅读(27)  评论(0编辑  收藏  举报