Not Too Many Balls-ABC332G

· · 题解

题目大意

给定 n 种球和 m 个盒子,对于第 i 种球有 a_i 个,第 i 个盒子的容量为 b_i,且对于所有满足 1 \le i \le n1 \le j \le m 的整数对 (i, j),第 j 个球盒子最多放 i \times j 个第 i 种球,求最大的放置数。

### Sol 不难想到网络流算法。 建立源汇点,对于每种球和每一个盒子都新建一个点,源点向球连边,流量为 $a_i$,盒子向汇点连边,流量为 $b_i$,球向盒子连边,流量为 $i \times j$,答案即为最大流。 在此数据范围下,网络流复杂度会炸,由于这个图的结构特殊性,考虑用 $dp$ 模拟网络流。 先将最大流转为最小割,则我们所求即为: $$ \min (\sum_{i \in X}a_i + \sum_{j \in Y}b_j+\sum_{i \notin X}\sum_{j \notin Y}ij) $$ 我们设 $\sum_{i \notin X}i = k$,则对于所有满足 $\sum_{i \notin X}i = k$ 的集合 $X$ 求出最小的 $\sum_{i \in X}a_i$,显然背包解决即可,则原式化为: $$ dp_k + k\sum_{j \notin Y}j + \sum_{j \in Y}b_j $$ 对于每一个可能的 $k$,贪心地取 $\min(b_j, jk)$,不难发现将 $b$ 按照 $\lfloor{b_j \over j}\rfloor$ 排序,则取 $b_j$ 和取 $jk$ 的部分为一段前缀和一段后缀,在枚举 $k$ 的过程中同时维护两部分的分界点即可。 由于在递增枚举 $k$ 的过程中,分界点也是单调递增的,最多移动 $m$ 次,保证了复杂度的正确性。 时间复杂度 $O(n ^ 3 + m)$。 ### Code ```cpp #include<bits/stdc++.h> using namespace std; #define int long long const int N = 5e2 + 5, M = 5e5 + 5; int a[N], b[M], dp[N * N], n, m, Ans = 1e18, pos[M]; bool compare(int i, int j){return (int)(b[i] / i) < (int)(b[j] / j);} signed main(){ cin >> n >> m; for(int i = 1; i <= n; i++)cin >> a[i]; for(int i = 1; i <= m; i++)cin >> b[i]; memset(dp, 0x3f, sizeof dp); dp[0] = 0; for(int i = 1; i <= n; i++) for(int k = n * (n + 1) / 2; k >= i; k--) dp[k] = min(dp[k], dp[k - i] + a[i]);//01背包 for(int i = 1; i <= m; i++)pos[i] = i; sort(pos + 1, pos + m + 1, compare);//按照b[j] / j升序排序 int sum1 = m * (m + 1) / 2, sum2 = 0, loc = 1; for(int i = 0; i <= n * (n + 1) / 2; i++){ while(i * pos[loc] > b[pos[loc]]){//维护分界点 sum1 -= pos[loc]; sum2 += b[pos[loc]]; loc++; } Ans = min(Ans, dp[n * (n + 1) / 2 - i] + i * sum1 + sum2);//维护答案 } cout << Ans << endl; return 0; } ```