题意:
现在有一张n*n的格子,每次会在x y位置加一个点,如果这个位置有点了就删掉,每个点每次能够往
走一格,最终到达第x行,并且每个格子只能有一个点。问你每次加入点之后,至少要在n列的基础上加多少列才能让所有点都走到第k行。
题解:
那么这道题目翻译一下就是,给你x y相当于现在有一个横轴,你要在第abs(x-k)+y个位置及之后找一个位置上没没有点并且能够装下它。那么暴力的找肯定是不行的,想了很多解法都是要么时间复杂度太大,要么不满足性质,但是它相当于单点修改,于是可以考虑线段树优化。
那么我们知道,线段树合并是将左右区间的状态转移到当前节点的状态,那么首先我们需要知道的是,
sum:表示这个区间里有多少个点
mx:表示这个区间最右边到哪(不超过这个区间的右端点)
那么光知道了mx还不够,因为会有很多个值在同一个点的情况,于是
ex:表示这个区间右边超过边界的值有几个
那么就可以进行转移了,要考虑很多的情况,其中最重要的就是想到左区间的ex会将右区间的所有空给填满的情况
all表示这个区间最右端到哪,space表示这个区间的未放点的数量有多少
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+5;
int mx[N*4],sum[N*4],ex[N*4];
void push_up(int l,int r,int root){
sum[root]=sum[root<<1]+sum[root<<1|1];
int mid=l+r>>1,all=max(0,mx[root<<1|1]-mid+ex[root<<1|1]),space=max(0,all-sum[root<<1|1]);
ex[root]=max(ex[root<<1|1],ex[root<<1]-(max(all,r-mid)-sum[root<<1|1])+ex[root<<1|1]);
mx[root]=max(mx[root<<1]+ex[root<<1],mx[root<<1|1]+ex[root<<1|1]);
mx[root]=max(mx[root],mx[root<<1|1]+ex[root<<1|1]+max(0,ex[root<<1]-space));
mx[root]=min(mx[root],r);
}
void update(int l,int r,int root,int p,int v){
if(l==r){
sum[root]+=v;
ex[root]=max(0,sum[root]-1);
mx[root]=sum[root]?r:0;
return ;
}
int mid=l+r>>1;
if(mid>=p)
update(l,mid,root<<1,p,v);
else
update(mid+1,r,root<<1|1,p,v);
push_up(l,r,root);
}
map<int,int>mp[N];
int main()
{
int n,k,m;
scanf("%d%d%d",&n,&k,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
int p=abs(x-k)+y;
if(mp[x][y]==1)
update(1,N-1,1,p,-1),mp[x][y]=0;
else
update(1,N-1,1,p,1),mp[x][y]=1;
printf("%d\n",max(0,mx[1]-n));
}
return 0;
}