题解:CF2234G Stripe, Token and Two Players
CF2234G(Codeforces Round 1102 (Div. 2) G) 题解
中文题意
有一个包含
两名玩家进行游戏。在每一步中,玩家依次执行以下操作:
- 假设棋子当前位于编号为
i 的格子上。 - 玩家可以将棋子的力量值增加
0 到a_i 之间的任意整数(包含0 和a_i )。 - 然后,玩家将棋子向前移动一个不超过棋子当前力量值的正整数步,且移动后棋子不能移出该长条(即不能越过编号
n + 1 的格子)。
在移动后使棋子恰好落在编号为
在双方都采取最优策略的情况下,谁将获胜?
思路
首先,可以发现力量值超过
这是正解的基础。
使用瞪眼法可以注意到,必败态的数量不会很多。可以发现,
接下来思考如何判断一个状态是否是必败态。如果根据上面的 DP 方程,我们实际需要实现一个『单点修改,对一个梯形的范围内取与』的数据结构,难以实现。因此考虑将查询的复杂度分摊给修改。我们将『离开第
虽然此时我们需要的数据结构仍然是二维的,但是我们能发现,查询只是一维的查询,而且我们枚举
我们可以维护一个集合,初始拥有
具体实现中,我们可以使用两个 set,其中第一个维护所有的连续段,第二个维护所有连续段的长度。查询时只需要不断查询第二个集合中的最大长度,直到最大长度小于等于
由于必败态数量只有
此外,也可以使用线段树维护『最长连续段的长度及其左端点』,也能通过本题,时间复杂度也是
代码
::::info[代码]
#include <iostream>
#include <queue>
#include <set>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int t, n, ans, a[N];
priority_queue<pii> pq;
set<pii> st1, st2;
void insert(int l, int r) { st1.emplace(l, r), st2.emplace(r - l + 1, l); }
void erase(int l, int r) { st1.erase(st1.find(pii(l, r))), st2.erase(st2.find(pii(r - l + 1, l))); }
void insert(int x) {
set<pii>::iterator nxt = st1.lower_bound(pii(x, x)), pre = (nxt == st1.begin() ? nxt : prev(nxt));
int l, r;
if (nxt != st1.end() && nxt != st1.begin() && nxt->first == x + 1 && pre->second == x - 1) {
l = pre->first, r = nxt->second;
erase(pre->first, pre->second), erase(nxt->first, nxt->second);
} else if (nxt != st1.end() && nxt->first == x + 1) {
l = x, r = nxt->second;
erase(nxt->first, nxt->second);
} else if (nxt != st1.begin() && pre->second == x - 1) {
l = pre->first, r = x;
erase(pre->first, pre->second);
} else
l = r = x;
insert(l, r);
}
void fail(int i, int k) {
if (i == 1 && k == 1)
ans = 2;
pq.emplace(i - k - 1, k);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
for (cin >> t; t; --t) {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
ans = 1, st1.clear(), st2.clear();
while (!pq.empty())
pq.pop();
for (int i = 1; i <= n; ++i)
fail(n + 1, i);
for (int i = n; i; --i) {
for (; !pq.empty() && pq.top().first >= i; pq.pop())
insert(pq.top().second);
while (!st2.empty() && st2.rbegin()->first > a[i]) {
int l = st2.rbegin()->second, r = st1.lower_bound(pii(l, l))->second;
erase(l, r);
for (int j = l; r - j >= a[i]; ++j)
fail(i, j);
insert(r - a[i] + 1, r);
}
}
cout << ans << "\n";
}
}
::::