这个题很顶。
我们贪心的想,移动后的棋子 j 最少 + abs(x - k) 。首先我们令 f(j) 为第 j 行及以上的棋子个数。于是我们得到溢出棋盘的棋子数量,也就是要添加的行数为 max(0, f(j) + j - n - 1)。
此时我们利用线段树实现区间更新,加入棋子就 +1 ,移走棋子就 -1 。更新的范围是(1, maxy)(maxy就是已存在的棋子中移动后的高度最高的那个)。因为大于 n 的线段树的部分结果不为0(当 j > n , j - n - 1可能大于0影响结果),需要精确的查询加入的棋子 f(j) + j - n - 1 最大的那个
注意:
预防棋盘为空,预留静态数组够大
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long LL;
const LL maxn = 2e5+5;
unordered_map<LL, bool> visit;
set<int> maxy;
int t[8*maxn], lazy[8*maxn]; // 要更新的区域是2*n,而线段树要开4倍大小
int n, k, m, x, y;
int cnt[2*maxn];
void pushup(int rt){t[rt] = max(t[rt<<1], t[rt<<1|1]);}
void build(int l, int r, int rt){
if(l == r) t[rt] = 0 + l - 1 - n; // 溢出棋盘的棋子数量
else{
int m = (l + r)/2;
build(l, m, rt<<1);
build(m + 1, r, rt<<1|1);
pushup(rt);
}
}
void pushdown(int rt){
if(lazy[rt]){
int rt1 = rt<<1, rt2 = rt<<1|1;
t[rt1] += lazy[rt];
t[rt2] += lazy[rt];
lazy[rt1] += lazy[rt];
lazy[rt2] += lazy[rt];
lazy[rt] = 0;
}
}
void update(int l, int r, int L, int R, int k, int rt){
if(L <= l && r <= R){
t[rt] += k;
lazy[rt] += k;
return;
}
pushdown(rt);
int mid = (l + r)/2;
if(L <= mid) update(l, mid, L, R, k, rt<<1);
if(R > mid)update(mid + 1, r, L, R, k, rt<<1|1);
pushup(rt);
}
int query(int l, int r, int L, int R, int rt){
if(L <= l && r <= R)
return t[rt];
pushdown(rt);
int mid = (l + r)/2;
int ans = 0;
if(L <= mid) ans = max(ans, query(l, mid, L, R, rt<<1));
if(R > mid) ans = max(ans, query(mid + 1, r, L, R, rt<<1|1));
pushup(rt);
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k >> m;
build(1, 2*n, 1);
n *= 2;
LL temp;
for(int i = 1; i <= m; i++){
cin >> x >> y;
temp = x*maxn + y;
y += abs(x - k);
if(visit[temp]){
visit[temp] = false;
cnt[y]--;
if(!cnt[y])maxy.erase(y);
update(1, n, 1, y, -1, 1);
}else{
visit[temp] = true;
cnt[y]++;
maxy.insert(y);
update(1, n, 1, y, 1, 1);
}
if(maxy.size() == 0) cout << 0 << endl; // 无棋子
else cout << query(1, n, 1, *maxy.rbegin(), 1) <<endl;
}
return 0;}