「CF1458E」Nim Shortcuts

CF1458E

一个代码比较简洁的偏移量做法。

考虑对于一个状态 \((x,y)\),其是必败点当且仅当该点正下方和正左方的点全都是必胜点。

因此一列(行)内除去快捷点,最多会有一个必败点,记为自然点。

在没有快捷点的情况下,容易发现必败点为 \((x,x)\)

加入快捷点后,自然点会发生偏移。

由于坐标范围很大,不可能枚举所有 \(x\),故考虑扫描线,按 \(x\) 从小到大排序确定每一列的必败点。

对于当前列 \(x\),维护一个偏移量 \(\Delta\),即当前列的自然点纵坐标为 \(x+\Delta\)

一个快捷点最多能使它后面的自然点的纵坐标偏移 \(1\),因此 \(\Delta\) 不会超过 \(n\)

1

上图橙色为快捷点,黑色为自然点,具体实现参见代码注释,时间复杂度 \(\mathcal O(n \log n)\)

#include<bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define rep(i,l,r) for(int i=(l); i<=(r); ++i)
#define drep(i,r,l) for(int i=(r); i>=(l); --i)
using namespace std;
const int N=2e5+5;
int n,m,d,f[N];
set<int>S;
priority_queue<array<int,3>,vector<array<int,3>>,greater<>>Q;
signed main() {
	FASTIO;
    cin>>n>>m;
    rep(i,1-n,m) {
        int x,y;
        cin>>x>>y,Q.push({x,max(i,0),y});
    }
    while(Q.size()) {
        int r=Q.top()[0],t=0;
        while(S.size()&&*S.begin()<=r+d) S.erase(S.begin()),++d;
        /*
          在进入新的一列时,需要考虑 set 中所有 <=x+d 的快捷点带来的影响:
          每有一个这种点,d 就加 1,因为又多了一个被占用的纵坐标,自然点需要上移一位,如 (3,4),(1,5)。
        */
        set<int>E;
        while(Q.size()&&Q.top()[0]==r&&!Q.top()[1]) {
            int c=Q.top()[2]; Q.pop();
            E.insert(c);
            if(c<r+d) t=1; //本列没有自然点
            else if(c>r+d||t) S.insert(c); //则它暂时不影响当前列,但会在未来某个 x 造成偏移,因此将其存入 set 中等待后续处理
            //否则该快捷点充当自然点,无需额外操作
        }
        !t?E.insert(r+d),0:--d;
        //若本列没有自然点,d-1,如 (3,1) 导致后面自然点降低一格
        while(Q.size()&&Q.top()[0]==r) f[Q.top()[1]]=E.count(Q.top()[2]),Q.pop();
    }
    rep(i,1,m) cout<<(f[i]?"LOSE":"WIN")<<'\n';
}
posted @ 2026-06-18 15:22  in-bustle  阅读(12)  评论(1)    收藏  举报