P8859 冒泡排序
我回来了。
参考:https://www.luogu.com.cn/blog/_post/509374、https://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
*/
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/17584655.html