1 条题解
-
0
CF1989D Smithing Skill 题解
题意简述
有 种武器和 种材料。锻造第 种武器消耗 个同种材料,熔毁该武器返还 个材料()。每次锻造得 经验,每次熔毁也得 经验。初始第 种材料有 个()。
你可以无限次锻造与熔毁,任意武器可用任意材料。求能获得的最大总经验值。
。
转化为循环模型
将锻造+熔毁视为一个循环:消耗 ,返还 ,净消耗 ,获得 经验。
锻造的门槛是当前材料数必须 。一次循环后材料数变为 。
如果一直用同一种武器,从 开始能完成的循环次数为:
$$\left\lfloor \frac{c - a_i}{d_i} \right\rfloor + 1 \quad (c \ge a_i) $$等价于 。
有效武器的筛选
对于同一个净消耗 ,门槛 越小越好。令
(若不存在则为 )。
若 且 ,则武器 被武器 完全支配。去除被支配的选项后,剩下的武器满足: 递增时, 递减。
构造两个数组:
- :第 个有效武器的门槛 (严格递减);
- :第 个有效武器的净消耗 (严格递增)。
DP 计算
设 表示手上有 个同种材料时,能完成的最大循环次数。
对于给定的 ,在所有满足 的武器中应选哪个?材料多时用 小的武器(循环次数 ),材料少时只能用门槛低的武器( 大)。由于 " 越大 越小",满足 的武器恰好是序列的一段后缀。用指针 找到后缀起点,然后转移:
c = i; f[i] += (c - q[now]) / p[now]; // 整批循环 c = (c - q[now]) % p[now] + q[now]; // 剩余材料 while (c >= q[now]) c -= p[now], f[i]++; // 可能还有最后一次 f[i] += f[c]; // 递归到更小的状态大状态拆成整除部分 + 余数部分,余数递归到已算好的 。
回答询问
对于每种初始材料 :
- 若 (最优武器门槛都不够):直接 ;
- 否则,先用最优武器批量消耗到 ,再加 。
if (c < q[1]) { ans += f[c]; continue; } ans += (c - q[1]) / p[1]; c = (c - q[1]) % p[1] + q[1]; while (c >= q[1]) c -= p[1], ans++; ans += f[c];最终答案 = 总循环次数 。
复杂度
- 预处理 :
- 构建 :()
- DP :
- 回答询问:
总复杂度 ,足以通过。
参考代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1500000; int n, m; ll a[N], b[N], s[N], q[N], p[N], f[N], cnt = 0, now = 0, ans = 0; inline ll read() { ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } int main() { for (int i = 0; i <= 1000000; i++) s[i] = 1e9; n = read(); m = read(); for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 1; i <= n; i++) b[i] = read(); for (int i = 1; i <= n; i++) { ll d = a[i] - b[i]; if (a[i] < s[d]) s[d] = a[i]; } cnt = 0; q[0] = 1e9; p[0] = 0; for (int i = 1; i <= 1000000; i++) { if (s[i] < q[cnt]) { q[++cnt] = s[i]; p[cnt] = i; } } now = cnt; for (int i = 1; i <= 1000000; i++) { while (q[now] <= i && now > 0) now--; if (now < cnt) now++; else continue; ll c = i; f[i] += (c - q[now]) / p[now]; c = (c - q[now]) % p[now] + q[now]; while (c >= q[now]) c -= p[now], f[i]++; f[i] += f[c]; } for (int i = 1; i <= m; i++) { ll c = read(); if (c < q[1]) { ans += f[c]; continue; } ans += (c - q[1]) / p[1]; c = (c - q[1]) % p[1] + q[1]; while (c >= q[1]) c -= p[1], ans++; ans += f[c]; } printf("%lld\n", ans * 2); return 0; }
- 1
信息
- ID
- 6890
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者