1 条题解
-
2
(这个题好厉害,让我感叹 MXOJ 比赛的质量)
不妨设 表示此时最小的可操作位置上面有 个棋子,下一个位置有 个,再下一个位置有 个棋子,需要最小/最大化操作数时的答案。
这个状态的转移中,只要 就会一直让 并让 或 (取较优的那个),知道 变成 或者 。所以说不妨去掉 一维,直接考虑 。
此时状态记作 。令 $t = \lfloor \frac{i}{2} \rfloor, nxtop = op \oplus [t \bmod 2 = 1]$,则有转移 。当务之急便是确认在两人足够聪明的情况下, 是多少,这样便能记忆化搜索了。
考虑如下问题:
有一个类似帕斯卡三角形的图案,第 层有 个顶点(记作 ),两人交替从顶点往下走,每一步可以向左()或者向右(),总共走 步,第 层的节点有权值,一个人想最大化终点的权值,一个人想最小化。问两人的策略。
我们分类讨论:
- 是偶数,则后手通过每一步与先手相反的方法,必定可以选到 。此外还可以选到 和 中对自己来说较劣的一个结果;
- 是奇数,则后手通过同样的方法可以限制先手只能选到 和 中对先手较优的结果。
简单讨论一下可以证明这个结论的充要性,此处略去。
那么一个状态最多扩展出三个状态,且三个状态后续的扩展有重叠,最多扩展 轮(两次扩展总数减半),总复杂度是一个关于 的多项式,可以通过。
写法参考了出题人的代码。
/* NE_Cat 4.1 */ #include <bits/stdc++.h> #define lowbit(x) ((x) & (-(x))) using namespace std; int T; long long n; map < tuple < long long, long long, bool >, long long > f; inline long long work(long long i, long long j, bool op) { if(!i && !j) return 0; if(f.count(make_tuple(i, j, op))) return f[make_tuple(i, j, op)]; if(i <= 1) return f[make_tuple(i, j, op)] = work(j, 0, op); long long t = i / 2; int nxt_op = op ^ (t & 1); if(op == false) { if(t % 2 == 1) return f[make_tuple(i, j, op)] = t + min(work(j + t / 2 + 1, t / 2, nxt_op), work(j + t / 2, t / 2 + 1, nxt_op)); else return f[make_tuple(i, j, op)] = t + max(work(j + t / 2, t / 2, nxt_op), min(work(j + t / 2 - 1, t / 2 + 1, nxt_op), work(j + t / 2 + 1, t / 2 - 1, nxt_op))); } else { if(t % 2 == 1) return f[make_tuple(i, j, op)] = t + max(work(j + t / 2 + 1, t / 2, nxt_op), work(j + t / 2, t / 2 + 1, nxt_op)); else return f[make_tuple(i, j, op)] = t + min(work(j + t / 2, t / 2, nxt_op), max(work(j + t / 2 - 1, t / 2 + 1, nxt_op), work(j + t / 2 + 1, t / 2 - 1, nxt_op))); } } inline void sol() { cin >> n; cout << work(n, 0, 0) << '\n'; } int main() { // freopen("text.in", "r", stdin); // freopen("prog.out", "w", stdout); ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> T; while(T--) sol(); return 0; } /* */
- 1
信息
- ID
- 107
- 时间
- 3000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- 递交数
- 57
- 已通过
- 14
- 上传者