大意: 给定01串, 两人轮流操作, Tokitsukaze先手. 每次操作可以选择长为$k$的区间, 全部替换为$0$或$1$, 若替换后同色则赢. 求最后结果.
先判断第一步是否能直接赢, 不能的话若所有后继都是必败则必败, 否则平局.
正确性很显然, 因为一次操作不能直接赢的话, 只要模仿对手操作一定能平局.
那么问题就转化为判断一步操作后是否能赢.
假设$0$的最大范围为$[L[0],R[0]]$,$1$的最大范围为$[L[1],R[1]]$, 那么只要操作前$R[0]-L[0]+1le k$或$R[1]-L[1]+1le k$那么一定必胜.
然后用带撤销的线段树枚举所有后继模拟即可.
#include <iostream> #include <cstdio> #define REP(i,a,n) for(int i=a;i<=n;++i) #define lc (o<<1) #define rc (lc|1) #define mid ((l+r)>>1) #define ls lc,l,mid #define rs rc,mid+1,r using namespace std; const int N = 1e6+10; int n,k,clk,tim[N<<2]; char s[N]; struct _ { int L[2],R[2]; void upd(int v, int l, int r) { L[v]=l,R[v]=r; L[!v]=1e9,R[!v]=-1e9; } _ operator + (const _ & rhs) const { _ ret; REP(i,0,1) { ret.L[i]=min(L[i],rhs.L[i]); ret.R[i]=max(R[i],rhs.R[i]); } return ret; } } tr[N<<2],tmp[N<<2]; void build(int o, int l, int r) { if (l==r) return tmp[o].upd(s[l]=='1',l,r); build(ls),build(rs); tmp[o]=tmp[lc]+tmp[rc]; } void upd(int o) { if (tim[o]!=clk) tr[o]=tmp[o],tim[o]=clk; } void update(int o, int l, int r, int ql, int qr, int v) { upd(o); if (ql<=l&&r<=qr) return tr[o].upd(v,l,r); else { upd(lc),upd(rc); if (mid>=ql) update(ls,ql,qr,v); if (mid<qr) update(rs,ql,qr,v); tr[o]=tr[lc]+tr[rc]; } } int chk() { REP(i,0,1) if (tr[1].R[i]-tr[1].L[i]+1<=k) return 1; return 0; } int work() { scanf("%d%d%s", &n, &k, s+1); build(1,1,n); ++clk,upd(1); if (chk()) return 1; int cnt = 0; REP(i,1,n-k+1) { int f = 0; ++clk, update(1,1,n,i,i+k-1,1), f += chk(); ++clk, update(1,1,n,i,i+k-1,0), f += chk(); if (f==2) ++cnt; } return cnt==n-k+1?0:-1; } int main() { int t = work(); if (t==1) puts("tokitsukaze"); else if (t==-1) puts("once again"); else puts("quailty"); }