题意
你有块钱, 当前等级为内的任意整数.
需要构造一个的任务序列. 对于每个任务的花费是: 若本任务的y比前一个任务的y大, 则为, 否则为. 每个任务的收益是: 若当前技能等级等于, 则技能等级提升. 要求总花费不超过初始财产的限制下, 这个操作序列能将任意的初始等级提升到至少.
分析
总花费是多少? 实际上就是任务序列所有的总和, 再加上提升的次数乘以.
这是个构造题, 先猜一猜一些可能的解. 比如我们构造这个难度序列, 总和为, 然而提升了非常多次(), 每次都要花, 这显然是非常不划算的.
如果我们这样构造呢? . 这样显然上升的次数是0, 但是由于没有地方中转, 每个任务我们都得将y提升到. 的总和将会是级别的, 不可接受.
所以说, 我们要让总花费最小, 重点在于平衡上升的次数与的总和.
如果说我们这样构造呢? , 每次上升1, 相当于把所有技能等级收集到了偶数上.再进行我们会发现, 虽然上升的次数多了一次, 然而由于我们提供了偶数作为中转, 的总和下降了非常多(虽然离我们的目标还很远). 进一步, 如果我们再把偶数收集到4倍数上, 4倍数收集到8倍数上…这样虽然上升的次数会增加, 但是总和会下降, 我们能不能找到一个比较好的极值呢? 很不幸并不能, 因为题目的限制非常严格. 然而我们很快会发现, 以上我们默认了”2”为基数, 而事实上”2”并非最好的基数.
解法
我们假设基数为. 这时候我们需要收集趟. 对于每趟来说: 先把所有模为1的数收集到模为2的数上, 再把模为2的数收集到模为3的数上…最后收集到模为0的数上, 这趟就算是跑完了. 我们会发现每一趟来说, 每个任务的都是1, 且几乎每个技能等级都会涉及一次(除了那些模m为0的技能等级). 所以, 每一趟中的总和我们可以看作. 每一趟上升多少次呢? 实际上就是收集的次数, 至多为.
所以我们可以列出总花费:
注意到不会有太多值, 我们可以手算枚举的值为时的情形, 得到的时候总花费可以低于(当然你想求导也是可以的). 此时. 于是我们就解决了这个问题.
AC代码
void solution() { /* code here */ int n; cin >> n; if (n == 4) {cout << R"(41 43 12 13 1)"; return; }
ll m = 63; ll base = 1; vector<int> vis(n + 1); vector<pair<int, int>> ans; ans.reserve(250000); for (int i = 0; i < 3; i++) { for (int j = 1; j < m; j++) { for (int k = n; k >= 1; k--) { if (vis[k]) { continue; } if ((k / base) % m != j) { continue; } vis[k] = 1; ans.emplace_back(k, base); } } base *= m; } cout << ans.size() << '\n'; for (const auto& [x, y] : ans) { cout << x << ' ' << y << '\n'; }}