题意
-
一开始有一个全部都是 1 的序列 a a a,你可以执行以下操作。
选择序列中的一个数 a i a_i ai,一个数 x x x, a i = a i + a i x a_i=a_i+\dfrac{a_i}{x} ai=ai+xai
如果经过多次操作后, a i = b i a_i=b_i ai=bi,你就可以获得 c i c_i ci 的价值。
问经过 k k k 次操作后,获得的价值最大是多少。
n ≤ 1 0 3 , k ≤ 1 0 6 , b i ≤ 1 0 3 , c i ≤ 1 0 6 n\le10^3,k\le10^6,b_i\le10^3,c_i\le10^6 n≤103,k≤106,bi≤103,ci≤106
思路
- 最大价值,想到背包,看看花费是什么,是操作数,现在问题变成求从 1 到 x 的最少操作数,可以利用 O ( ( max b i ) 2 ) ≈ 1 0 6 O((\max b_i)^2)\approx 10^6 O((maxbi)2)≈106 的最短路暴力预处理。
- 最短路预处理时,当当前数 > 1000 时就不再继续扩展最短路。
- 然后跑一个 01 背包。
- 预处理后知, v i ≤ 12 v_i\le 12 vi≤12。所以背包体积取个min: chkmin(V, 12 * n), 可以优化复杂度至: O ( n ⋅ min ( k , 12 × n ) ) ≈ 1 0 7 O(n\cdot\min(k,12\times n)) \approx10^7 O(n⋅min(k,12×n))≈107,可以通过。
代码
const int N = 1e6 + 5;
int n, V, v[N], w[N], b[N], f[N];
int main() {
mst(v, 0x3f); v[1] = 0;
queue<pii> q;
q.push({1, 0});
while(!q.empty()) {
auto [u, d] = q.front(); q.pop();
rep(i, 1, u) {
int t = u + u / i;
if(t > 1000) continue;
if(v[t] > v[u] + 1) {
v[t] = v[u] + 1;
q.push({t, v[t]});
}
}
}
int T; cin >> T;
while(T--) {
mst(f, 0);
cin >> n >> V;
rep(i, 1, n) cin >> b[i];
rep(i, 1, n) cin >> w[i];
chkmin(V, 12 * n);
rep(i, 1, n) per(j, V, v[b[i]]) chkmax(f[j], f[j - v[b[i]]] + w[i]);
cout << f[V] << endl;
}