题解 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  阅读(45)  评论(0)    收藏  举报