题解 CF1379F2 Chess Strikes Back (hard version)

数据结构之小清新思维题。

容易想到把 \(2n\times2m\) 棋盘中每个 \(2\times 2\) 的部分压缩,其中必须含有恰好一个棋子。

对于每个 \(2\times 2\) 分两种情况讨论(可能同时具备或不具备以下两种):

  1. 左上角不能用,记为 \(L\)

  2. 右下角不能用,记为 \(R\)

然后通过画图可以发现,如果一个点为 \(R\),那么它的左上方确定了,且不能含有 \(L\)。如果一个点为 \(L\),那么它的右下方确定了,且不能含有 \(R\)

所以可以得出结论,当且仅当没有一个 \(L\)\(R\) 的左上方时,存在合法的放置方法。

接下来就是数据结构的事了,方法有很多种,这里介绍线段树的做法。

我们根据横坐标建立线段树。

对于每个节点,维护 \((mi,ma,no)\) 分别表示 \(L\) 的最小值,\(R\) 的最大值,以及是否合法。

\(mi,ma\) 可以直接转移。\(no\) 可以根据左儿子的 \(no\),右儿子的 \(no\),以及左儿子 \(mi\) 和右儿子 \(ma\) 的大小关系确定。

最后还要在每行用 set 维护所有 \(L\)\(R\)

复杂度 \(O(n\log n)\)

code:

#include<bits/stdc++.h>
#define ls (id*2)
#define rs (id*2+1)
using namespace std;
const int N=2e5+5;
int n,m,q;
set<int>L[N],R[N];
struct node{int l,r,mi,ma,no;}t[4*N];
void build(int id,int l,int r){
  t[id].l=l;t[id].r=r;t[id].mi=1e9;
  if(l==r)return;
  int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);
}
void pushup(int id){
  t[id].mi=min(t[ls].mi,t[rs].mi);t[id].ma=max(t[ls].ma,t[rs].ma);
  t[id].no=t[ls].no|t[rs].no|(t[ls].mi<=t[rs].ma);
}
void change(int id,int w,int mi,int ma){
  if(t[id].l==t[id].r){
    t[id].mi=mi;t[id].ma=ma;t[id].no=(t[id].mi<=t[id].ma);
    return;
  }
  int mid=(t[id].l+t[id].r)>>1;
  if(w<=mid)change(ls,w,mi,ma);
  else change(rs,w,mi,ma);
  pushup(id);
}
int main(){
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n>>m>>q;
  build(1,1,n);
  while(q--){
    int x,y,wx,wy;cin>>x>>y;wx=(x-1)/2+1;wy=(y-1)/2+1;
    if(x&1){
      if(L[wx].count(wy))L[wx].erase(wy);
      else L[wx].insert(wy);
    }else{
      if(R[wx].count(wy))R[wx].erase(wy);
      else R[wx].insert(wy);
    }
    int mi=1e9,ma=0;
    if(L[wx].size())mi=(*L[wx].begin());
    if(R[wx].size())ma=(*--R[wx].end());
    change(1,wx,mi,ma);
    if(t[1].no)cout<<"NO"<<endl;
    else cout<<"Yes"<<endl;
  }
  return 0;
}

posted @ 2023-07-17 21:44  HQJ2007  阅读(23)  评论(0)    收藏  举报