Solution Set 【2023.12.12】
ABC332G Not Too Many Balls
可以转化为最大流模型,设节点 \(x_i\) 代表第 \(i\) 种球,\(y_j\) 代表第 \(j\) 个盒子。考虑如下建边方案:
- \(S \rightarrow x_i\),容量为 \(A_i\)
- \(x_i \rightarrow y_j\),容量为 \(i \times k\)
- \(y_j \rightarrow T\),容量为 \(B_j\)
可以发现该网络的最大流即为答案,但是由于边数是 \(\mathcal{O}(NM)\) 级别的,直接求最大流会超时。根据最大流最小割定理,若我们可以求出该网络的最小割,那么我们也可以获得答案,下面考虑如何求出最小割。
首先考虑如何表示出最小割的值,设点集 \(X = \left\{x_1, x_2, \cdots, x_N\right\}, Y = \left\{x_1, x_2, \cdots, x_N\right\}\),\(P\) 表示最小割方案中 \(X\) 中与 \(S\) 在同一连通块内的点集,\(Q\) 表示最小割方案中 \(Y\) 中与 \(S\) 在同一连通块内的点集。那么考虑三条边中都有哪些边被割掉,可以得出最小割的表达式:
设 \(k = \sum\limits_{x_i \in P} i, L = \dfrac{N\left(N + 1\right)}{2}\),通过枚举 \(x\) 的值,我们有:
其中最后一步转化是因为 \(Y\) 中的每个节点要么属于 \(Q\),要么属于 \(Y \setminus Q\),也就是其会产生 \(k \times j\) 或 \(B_j\) 的贡献,故可以直接取最小值。
对于上式的前半部分,即 \(f_k = \min\limits_{P \subseteq X \land \sum\limits_{x_i \in P} i = k}\sum\limits_{x_i \in X \setminus P} A_i\)。我们可以使用 \(\tt{01}\) 背包在 \(\mathcal{O}(NL)\) 的时间内求出对于 \(0 \le k \le L\) 的所有 \(f_k\) 的值。
对于上式的后半部分,即 \(g_k = \sum\limits_{y_j \in Y} \min\left\{k \times j, B_j\right\}\)。可以发现当 \(k = 0\) 时,所有的节点均会从 \(k \times j\) 来产生贡献,而由于 \(k \times j\) 随 \(k\) 的增长而单调递增,因此对于所有满足 \(y_j \in Y\) 的 \(j\),均存在一个 \(k_0\) 使得当 \(k \ge k_0\) 时,其会产生 \(B_j\) 而不是 \(k \times j\) 的贡献,所以有 \(\forall k \ge k_0, B_j \le k \times j\),解得 \(k_0 = \dfrac{B_j}{j}\)。因此我们对于每个 \(j\) 求出出其对应的 \(k_0\) 后遍历 \(0 \le k \le L\) 并求出 \(g_k\) 即可。
最终答案即为 \(\min\limits_{0 \le k \le L} f_k + g_{L - k}\)。复杂度为 \(\mathcal{O}(N^3 + M)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
constexpr valueType INF = std::numeric_limits<valueType>::max() >> 3;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M;
std::cin >> N >> M;
valueType const S = N * (N + 1) / 2;
ValueVector A(N + 1), B(M + 1);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
for (valueType i = 1; i <= M; ++i)
std::cin >> B[i];
ValueVector F(S + 1, INF);
F[0] = 0;
for (valueType i = 1; i <= N; ++i) {
for (valueType j = S; j >= i; --j)
F[j] = std::min(F[j], F[j - i] + A[i]);
}
ValueVector G(S + 1, 0);
ValueMatrix H(S + 1);
for (valueType i = 1; i <= M; ++i) {
valueType const pos = B[i] / i;
if (pos <= S)
H[pos].emplace_back(i);
}
{
valueType indexSum = M * (M + 1) / 2, valueSum = 0;
for (valueType i = 0; i <= S; ++i) {
G[i] += i * indexSum + valueSum;
for (auto const &iter : H[i]) {
indexSum -= iter;
valueSum += B[iter];
}
}
}
valueType ans = INF;
for (valueType i = 0; i <= S; ++i)
ans = std::min(ans, F[i] + G[S - i]);
std::cout << ans << std::endl;
return 0;
}
ARC125E Snack
同 ABC332G Not Too Many Balls,我们可以将其转化为最大流模型后求其最小割,化简后的最小割表达式为:
求 \(f_k = \min\limits_{P \subseteq X \land \left\lvert P \right\rvert = k} \sum\limits_{x_i \in X \setminus P} A_i\) 是平凡的,将 \(A\) 序列排序后做前缀和即可。
对于 \(g_k = \sum\limits_{y_j \in Y} \min\left\{k \times B_j, C_j\right\}\) 同 ABC332G Not Too Many Balls,可以得出 \(k_0 = \dfrac{C_j}{B_j}\)。
复杂度为 \(\mathcal{O}(N \log N + M)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
constexpr valueType INF = std::numeric_limits<valueType>::max() >> 3;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M;
std::cin >> N >> M;
ValueVector A(N + 1, 0), B(M + 1, 0), C(M + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
for (valueType i = 1; i <= M; ++i)
std::cin >> B[i];
for (valueType i = 1; i <= M; ++i)
std::cin >> C[i];
ValueVector F = A;
std::sort(F.begin(), F.end());
std::partial_sum(F.begin(), F.end(), F.begin());
ValueVector G(N + 1, 0);
ValueMatrix bucket(N + 1);
for (valueType i = 1; i <= M; ++i) {
valueType const pos = C[i] / B[i];
if (pos <= N)
bucket[pos].push_back(i);
}
valueType SumB = std::accumulate(B.begin(), B.end(), (valueType) 0), SumC = 0;
for (valueType i = 0; i <= N; ++i) {
G[i] = i * SumB + SumC;
for (auto const &iter : bucket[i]) {
SumB -= B[iter];
SumC += C[iter];
}
}
valueType ans = INF;
for (valueType i = 0; i <= N; ++i)
ans = std::min(ans, F[i] + G[N - i]);
std::cout << ans << std::endl;
return 0;
}