[CF2234G] Stripe, Token and Two Players 的题解
定义
称
最优操作下,一个状态是必败态,当且仅当其所有后继都是必胜态。
于是可以倒序维护,对于每个
然后因为必败态很少,可以直接暴力维护。官解用了 set,这里使用了线段树。寻找必败态相当于在线段树上查连续段。
时间复杂度
::::success[代码]
#include <bits/stdc++.h>
#define MSOD
using namespace std;
using ll = long long;
const int N = 1e5 + 5;
int n;
int a[N];
basic_string<int> bs[N];
struct SGT {
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((pl + pr) >> 1)
int len[N << 2], mx[N << 2], pre[N << 2], suf[N << 2];
void bld(int p, int pl, int pr) {
mx[p] = pre[p] = suf[p] = 0, len[p] = pr - pl + 1;
if(pl == pr) return;
bld(ls, pl, mid);
bld(rs, mid + 1, pr);
return;
}
void up(int p) {
pre[p] = (suf[ls] == len[ls] ? len[ls] + pre[rs] : pre[ls]);
suf[p] = (pre[rs] == len[rs] ? len[rs] + suf[ls] : suf[rs]);
mx[p] = max({mx[ls], mx[rs], suf[ls] + pre[rs]});
return;
}
void upd(int p, int pl, int pr, int pos, int d) {
if(pl == pr) return mx[p] = pre[p] = suf[p] = d, void();
if(pos <= mid) upd(ls, pl, mid, pos, d);
else upd(rs, mid + 1, pr, pos, d);
up(p);
return;
}
int qry(int p, int pl, int pr, int d) {
if(mx[p] < d) return -1;
if(pl == pr) return pl;
if(mx[ls] >= d) return qry(ls, pl, mid, d);
else if(suf[ls] + pre[rs] >= d) return mid - suf[ls] + 1;
else return qry(rs, mid + 1, pr, d);
}
int fnd() {
int res = qry(1, 0, n, mx[1]);
if(res != -1) upd(1, 0, n, res, 0);
return res;
}
} T;
void TEST() {
cin>>n;
for(int i = 1 ; i <= n ; i ++) cin>>a[i], bs[i].clear();
for(int i = 0 ; i < n ; i ++) bs[n - i] += i;
bool ok = true;
T.bld(1, 0, n);
for(int i = n ; i >= 1 ; i --) {
for(auto j : bs[i]) T.upd(1, 0, n, j, 1);
while(T.mx[1] > a[i]) {
int res = T.fnd();
if(i == 1 && res == 1) ok = false;
if(i - res - 1 >= 1) bs[i - res - 1] += res;
}
}
cout<<(ok ? 1 : 2)<<endl;
return;
}
signed main() {
ios::sync_with_stdio(false), cout.tie(0), cin.tie(0);
int TC = 1;
#ifdef MSOD
cin>>TC;
#endif
while(TC --) TEST();
return 0;
}
::::