对于单个set方案,每次分group时,如果有不同颜色可以把两个球分到同一个group,如果只剩一个颜色则只能一个球一个group。那么最优的分法是每次数量最少的球和数量最多的球分同一个group,直到分完或者分到只剩一种颜色的球,如果只剩一种颜色则这些球每一个成一个group。
对于单个set方案,假设 x x x 是方案里数量最多的颜色的球的数量, y y y 为其它球数量,则这个set方案的value(最小group数)为
max ( x , ⌈ x + y 2 ⌉ ) . \mathop{\max}(x,\lceil \frac{x+y}{2} \rceil). max(x,⌈2x+y⌉).
接下来,要得到题目所问结果,我们需要把所有可能方案的value求和。方案数有 2 n 2^n 2n 种,我们不可能一个一个枚举,这种涉及到求方案数的题可以考虑DP。
先把 a a a 数组从小到大排一个序,以保证第一次遍历到 a i a_i ai 时它是目前已考虑方案的最大值。而且题目保证 ∑ i = 1 n a i ≤ 5000 \sum_{i=1}^n a_i \le 5000 ∑i=1nai≤5000 ,可以考虑对球数遍历。
一种可行的状态转移函数是, f i , j f_{i,j} fi,j 的 i i i 表示遍历到的 a i a_i ai 的下标, j j j 表示当前球数, f i , j f_{i,j} fi,j 函数值表示这个状态的方案数,这个方案数用DP很容易求出。
我们用状态 j j j 代替之前式子中的其它球数量 y y y , a i a_i ai 由于排过序,遍历到的时候保证是set中数量最多的颜色的球的数量,则每一次访问一个状态要对结果加上
max ( a i , ⌈ a i + j 2 ⌉ ) × f i − 1 , j . \mathop{\max}(a_i,\lceil \frac{a_i+j}{2} \rceil) \times f_{i-1,j}. max(ai,⌈2ai+j⌉)×fi−1,j.
i i i 维度的空间可以用 j j j 的逆向遍历优化节省掉。最后得到了以下代码:
#include <bits/stdc++.h>
#define ll long long
#define MOD 998244353
using namespace std;
int n,a[5010];
ll f[5010];
int main() {
cin>>n;
for (int i=1;i<=n;++i) cin>>a[i];
sort(a+1,a+1+n);
memset(f,0,sizeof(f));
f[0] = 1;
ll ans = 0;
for (int i=1;i<=n;++i){
for (int j=5000-a[i];j>=0;--j){
ans = (ans + max(a[i],(int)ceil((double)(j+a[i])/2.0))*f[j]%MOD)%MOD;
f[j+a[i]] = (f[j+a[i]] + f[j])%MOD;
}
}
cout<<ans<<endl;
return 0;
}