题解/算法 {D. Colored Balls}
@LINK: https://codeforces.com/contest/1954/problem/D
;
对于某一个方案 (即选择了若干个颜色), 此时她是一个模板算法 (@LINK: https://editor.csdn.net/md/?not_checkout=1&articleId=137719326
匹配若干颜色小球), 如果你不知道正确解法 这题很容易出错…
.
比如{10,10,6}
, 他的答案 是可以O(1)
求出的; (如果你想出其他O(N)
的做法 有可能是错误的, 比如从大到小排序后 每次匹配相邻元素 这是错误的);
此时问题转换为: 对于序列A, 其任意一个子序列{Bi}
的价值为 max( max(Bi), (sum(B)+1)/2)
, 即 我们需要知道{Bi
序列的元素和, Bi
的最大值};
对A进行排序, 这样枚举A[i]
时 她是最大元素 其前面元素均<=A[i]
;
#错误做法#
令DP[i][j]
: 形如[..., A[i]]
的元素之和为j
的序列个数; 于是答案为DP[i][j] * 其价值(即max(A[i], (j+1)/2)
;
.
可问题在于, 你这样定义DP, 她的时间是N^3
的, 即对于(i,j)
她的前驱是(1/2/3/..., j-A[i])
, 会超时的;
于是 你想着 把DP定义为 以A[1/2/3/.../i]
结尾的序列个数, 这样时间就是N^2
的了 因为(i,j)
的前驱只有(i-1)
了; 可是此时 你最终得到DP后, 怎么求答案呢? 因为此时 对于DP[i][j]
你并不知道 她的末尾元素是啥…
但其实吧, 这是正确的…
.
最终的DP, 确实无法求出答案; 但是, 其实答案可以在DP构建的过程里 求出来的… 因为(i,j)
等于 (i-1,j) 和 (i-1, j-A[i])
而其中的(i-1, j-A[i])
这个方案 其实就表示以A[i]
结尾的 和为j的方案个数 (即对应之前的那个正确但超时的DP(i,j)
), 因此 此时可以求答案;
int N; cin>> N;
vector<int> A(N); for( auto & i : A){ cin>> i;}
sort(A.begin(), A.end());
using Mod_ = Modular<int,998244353>;
auto ___GetMinimalGroups = []( int _allCount, int _maxCount){
return std::max( (_allCount+1)/2, _maxCount);
};
static Mod_ DP[5003][5003];
memset( DP, 0, sizeof(DP));
Mod_ ANS = 0;
{
DP[0][A[0]] = 1;
ANS += (DP[0][A[0]] * (Mod_)___GetMinimalGroups(A[0],A[0]));
DP[0][0] = 1;
FOR_( i, 1, N-1){
memcpy( DP[i], DP[i-1], sizeof(DP[i]));
FOR_( j, A[i], 5000){
auto const& preDP = DP[ i-1][ j-A[i]];
auto & nexDP = DP[ i][ j];
nexDP += preDP;
//> `[..., A[i]]`(且和为`j`)的方案个数 等于`preDP`;
ANS += (preDP * (Mod_)___GetMinimalGroups(j, A[i]));
}
}
}