題目
n列n行(n<=2e5)的棋盤,第k(1<=k<=n)列是特殊列,
以下有m(m<=2e5)個操作,每次給出一個兵(x,y)(x列y行),
如果棋盤這個位置有兵,就拿掉原位置的兵,否則把它放入這個位置
兵的行走規則是可以從(x,y)走向(x,y+1),(x+1,y+1),(x-1,y+1),
即以走向下一行爲代價,保持列不變或走向相鄰列
對於每次操作後,都詢問一次,
若令當前棋盤上的所有兵都走到第k列上,棋盤最少需要擴充多少行
思路來源
https://www.cnblogs.com/limil/p/13204543.html
題解
首先,棋子走到第k列時,行的下限爲y+abs(x-k),稱其爲必要位置,大於等於該行的行也可
類似一個第幾分鐘有幾個人排隊,一分鐘只能結賬一個人,最終問所有人都能結賬完的最小時間問題,
該問題需要動態維護,考慮Hall定理,先把所有棋子都放到必要位置,
設爲第j行及第j行下方的棋子個數(即棋子位於列r,且r>=j),
若最終棋盤共x行,則需要對任意f(j)>0的行,滿足,
有,即,再減去n就是需要擴充的行數
於是開一個set<ll>cell維護哪些點在棋盤上,開一個set<ll>now維護當前移動到第k列的棋子所佔的位置,
後者用於求移動到第k列的棋子的最大位置pos,pos是滿足f(j)>0的最大的j
插入位置爲j時,[1,j]的f(j)+1,刪除同理
詢問時,若now爲空返回0,否則詢問[1,pos]的最大值x,減n之後就是答案
由於(n,n)走到特殊列k=1時,關鍵位置爲第2n-1行,所以開2*n的線段樹
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+10;
int n,k,m,x,y,num[N],pos;
set<ll>cell,now;
struct segtree{
int n;
struct node{int l,r,v,add;}e[N<<2];
#define l(p) e[p].l
#define r(p) e[p].r
#define v(p) e[p].v
#define a(p) e[p].add
void up(int p){v(p)=max(v(p<<1),v(p<<1|1));}
void bld(int p,int l,int r){
l(p)=l;r(p)=r;
if(l==r){v(p)=l-1;return;}
int mid=l+r>>1;
bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
up(p);
}
void init(int _n){n=_n;bld(1,1,n);}
void psd(int p){
if(a(p)){
a(p<<1)+=a(p),a(p<<1|1)+=a(p);
v(p<<1)+=a(p),v(p<<1|1)+=a(p);
a(p)=0;
}
}
void chg(int p,int ql,int qr,int v){
if(ql<=l(p)&&r(p)<=qr){
a(p)+=v;
v(p)+=v;
return;
}
psd(p);
int mid=l(p)+r(p)>>1;
if(ql<=mid)chg(p<<1,ql,qr,v);
if(qr>mid)chg(p<<1|1,ql,qr,v);
up(p);
}
int ask(int p,int ql,int qr){
if(ql<=l(p)&&r(p)<=qr)return v(p);
psd(p);
int mid=l(p)+r(p)>>1,res=0;
if(ql<=mid)res=max(res,ask(p<<1,ql,qr));
if(qr>mid)res=max(res,ask(p<<1|1,ql,qr));
return res;
}
}seg;
int main(){
scanf("%d%d%d",&n,&k,&m);
seg.init(2*n);
for(int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
pos=y+abs(x-k);
//printf("p:%d\n",pos);
if(cell.count(1ll*x*N+y)){//
cell.erase(1ll*x*N+y);
num[pos]--;
if(num[pos]==0){
now.erase(pos);
}
seg.chg(1,1,pos,-1);
}
else{
cell.insert(1ll*x*N+y);
num[pos]++;
if(num[pos]==1){
now.insert(pos);
}
seg.chg(1,1,pos,1);
}
if(now.empty())puts("0");
else printf("%d\n",max(0,seg.ask(1,1,*now.rbegin())-n));
}
return 0;
}