题解 CF1379F2 Chess Strikes Back (hard version)
数据结构之小清新思维题。
容易想到把 \(2n\times2m\) 棋盘中每个 \(2\times 2\) 的部分压缩,其中必须含有恰好一个棋子。
对于每个 \(2\times 2\) 分两种情况讨论(可能同时具备或不具备以下两种):
-
左上角不能用,记为 \(L\)。
-
右下角不能用,记为 \(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;
}

浙公网安备 33010602011771号