大致题意就是数有多少棵 $n$ 个点的二叉树,编号为 $1\sim n$ 的排列,要求有一个叶子编号为 $R$,并且二叉树满足大根堆的性质(父亲大于所有子节点)。对 $m$ 取模。$1\le n, R \le 2021$。
首先可以转化为小根堆,此时要求的叶子节点就是 $L=n-R+1$。设 $t(n)$ 表示在不考虑叶子限制的情况下 $n$ 个点的满足小根堆性质的二叉树数目,那么可以枚举左右儿子的点的个数即可,就有
$$
t(i) = \sum\limits_{j=1}^{(i-1)/2}t(j)t(i-1-j)\binom{i-1}{j}
$$
然后注意一下本题左右子树是可交换的,那么就要在两棵子树大小相等时除以 2。而这题恶心的地方在于 $m$ 不一定是质数,那么首先就保存模 $2m$ 的组合数,除以 2 时直接除即可。
接下来考虑带有叶子的限制。考虑 $t(n)$ 的另一种推法,即此时 $L$ 不一定是叶子节点。那么可以枚举 $L$ 下面有多少节点(即多少节点比 $L$ 大),设有 $k$ 个,然后这些点带上 $L$ 的贡献就是 $t(k+1)$。然后还剩下 $n-k$ 个点以 $L$ 为叶子,那么就有:
$$
t(i) = \sum\limits_{k=0}^{i-L}\binom{i-L}{k} t(k+1) ans(i-k)
$$
反解出 $ans(i) = t(i) - \sum\limits_{k=1}^{i-L}\binom{i-L}{k} t(k+1) ans(i-k)$
最后答案就是 $ans(n)$。
总之这个题很奇妙......貌似还有更简单的推法(不会,我爬了)
#include <bits/stdc++.h>
#define ll long long
#define ls id << 1
#define rs id << 1 | 1
#define mem(array, value, size, type) memset(array, value, ((size) + 5) * sizeof(type))
#define memarray(array, value) memset(array, value, sizeof(array))
#define fillarray(array, value, begin, end) fill((array) + (begin), (array) + (end) + 1, value)
#define fillvector(v, value) fill((v).begin(), (v).end(), value)
#define pb(x) push_back(x)
#define st(x) (1LL << (x))
#define pii pair<int, int>
#define mp(a, b) make_pair((a), (b))
#define Flush fflush(stdout)
#define vecfirst (*vec.begin())
#define veclast (*vec.rbegin())
#define vecall(v) (v).begin(), (v).end()
#define vecupsort(v) (sort((v).begin(), (v).end()))
#define vecdownsort(v, type) (sort(vecall(v), greater<type>()))
#define veccmpsort(v, cmp) (sort(vecall(v), cmp))
using namespace std;
const int N = 2050;
const int inf = 0x3f3f3f3f;
const ll llinf = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
clock_t TIME__START, TIME__END;
void program_end()
{
#ifdef ONLINE
printf("\n\nTime used:
system("pause");
#endif
}
int n, r, m;
ll C[N][N], C2[N][N];
ll t[N], ans[N];
void preC()
{
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])
}
C2[0][0] = 1;
for (int i = 1; i < N; ++i)
{
C2[i][0] = 1;
for (int j = 1; j <= i; ++j)
C2[i][j] = (C2[i - 1][j] + C2[i - 1][j - 1])
}
}
inline void solve()
{
scanf(
preC();
r = n - r + 1;
t[0] = t[1] = 1;
for (int i = 2; i <= n; ++i)
for (int j = 0; j <= ((i - 1) >> 1); ++j)
{
ll x = ((i & 1) && j == ((i - 1) >> 1)) ? C2[i - 1][j] / 2 : C[i - 1][j];
x = x * t[j]
t[i] = (t[i] + x)
}
for (int nn = r; nn <= n; ++nn)
{
ll res = 0;
for (int k = 1; k <= nn - r; ++k)
{
ll zu = C[nn - r][k] * ans[nn - k]
res = (res + zu)
}
res = (t[nn] - res + m)
ans[nn] = res;
}
printf(
}
int main()
{
TIME__START = clock();
int Test = 1;
// scanf(
while (Test--)
{
solve();
// if (Test)
// putchar('\n');
}
TIME__END = clock();
program_end();
return 0;
}
Comments NOTHING