简要题意:
长度为 \(n\) 的序列 \(a\),初始均为 \(0\). 可以花 \(C_i\) 的代价使 \(a_i \gets a_i + 1\). 求最少花多少代价能使 \(\prod a_i \geq m\)(其中不计 \(a_i = 0\) 的积)。
\(n \leq 200, C_i \leq 125, K_i \leq 10, m \leq 10^{17}\).
个人觉得其实可以先算一下代价的最大值。
很显然代价最大值为 \(\sum C_i \cdot K_i\),也就是 \(2.5 \times 10^5\) 左右。
于是背包 \(\text{dp}\) 应运而生。
很显然,令 \(f_{i,j}\) 表示前 \(i\) 个数,换 \(j\) 的代价所能达到的最大乘积(除 \(0\)). 则很容易得到
\[f_{i,j} = \max(f_{i-1,j}, \max_{k=1}^{K_i} f_{i-1,j-k \cdot c_i} \cdot k)
\]
时间复杂度:\(\mathcal{O}(n \sum C_i \cdot K_i)\),非常稳。
空间可能会爆,那就滚动数组,把 \(i\) 这一维省掉。但要注意倒序枚举。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 1;
const int M = 2e2 + 1;
int n; ll m;
int K[M], c[M];
ll f[N];
int main() {
scanf("%d %lld",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d", K + i);
for(int i = 1; i <= n; i++) scanf("%d", c + i);
f[0] = 1;
for(int i = 1; i <= n; i++)
for(int j = N - 1; j > 0; j--)
for(int k = 1; k <= K[i] && k * c[i] <= j; k++)
f[j] = max(f[j], 1ll * f[j - k * c[i]] * k);
for(int i = 1; i < N; i++)
if(f[i] >= m) return printf("%d\n",i), 0;
}