题解:AT_mujin_pc_2018_f チーム分け
神经 NOIP 模拟赛把这题放 T1,以为是道签到题结果两个小时才做出来,给我急死了。
首先因为
然后是难点:发现这是一个 DP,并且设计出 DP 状态。(我是试出来的,大家不要学我。)
总之 DP 状态是:
枚举
如果不算,那么结果和
如果算,那么枚举
接下来需要一些组合数知识,我们先把这剩下的
这时事先把
a 从小到大排序的作用就体现出来了,这可以保证当前正在处理的a_i 无论与前面的人怎么分组,这个a_i 都一定是组内最小的。只要让k \le a_i ,就能保证组内所有人的条件都能满足。
综上所述,总的状态转移方程如下:
直接计算的时间复杂度是
f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
f[i][0] = 1, f[i][1] = i;
for (int j = 2; j <= i; j++)
{
f[i][j] = f[i - 1][j];
for (int k = 1; k <= min(a[i], j); k++)
(f[i][j] += f[i - 1][j - k] * C[i - 1 - (j - k)][k - 1]) %= MOD;
}
}
但是模拟赛考场上毒瘤出题人搬题人不知道是不是为了卡
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef unsigned long long ULL;
constexpr int MAXN = 1005;
constexpr ULL MOD = 998244353;
int n, a[MAXN];
ULL f[MAXN][MAXN], C[MAXN][MAXN];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= i; j++)
C[i][j] = j ? (C[i - 1][j] + C[i - 1][j - 1]) % MOD : 1;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1, greater<int>());
f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
f[i][0] = 1, f[i][1] = i;
for (int j = 2; j <= i; j++)
{
f[i][j] = f[i - 1][j];
int ul = min(a[i], j);
for (int k = 1, cnt = 0; k <= ul; k++)
{
f[i][j] += f[i - 1][j - k] * C[i - 1 - (j - k)][k - 1];
if (++cnt == 17) f[i][j] %= MOD, cnt = 0;
}
f[i][j] %= MOD;
}
}
cout << f[n][n] << '\n';
return 0;
}