Educational CF Round 90___G. Pawns —— 线段树

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n ∗ n n * n nn 的图, m m m 次操作,规定第 k k k 列为特殊列
    每次操作选定一个 ( x , y ) (x, y) (x,y),若没有棋子则出现一颗棋子,反之消失
    棋子 ( x , y ) (x, y) (x,y) 只能往 ( x + 1 , y − 1 ) 、 ( x + 1 , y ) 、 ( x + 1 , y + 1 ) (x+1, y-1)、(x+1, y)、(x+1, y+1) (x+1,y1)(x+1,y)(x+1,y+1) 三个方向走
    所有棋子都必须走到第 k k k 列,若棋盘的行数不够,可以增加行
    每个位置最多只能放一颗棋子
    问所有棋子都到第 k k k 列后,增加的最小行数

解题思路:

    棋子 ( x , y ) (x, y) (x,y) 走到第 k k k 列时,最小的行数为 x + a b s ( y − k ) x + abs(y - k) x+abs(yk)
    这样就知道了所有棋子走到第 k k k 列时行的位置
    但是重复的位置要继续往上走,彼此之间相互影响,很难维护

    设 f ( j ) f(j) f(j) 为 第 j j j 行以上有多少颗棋子
    那么 n n n 行的棋盘能装下的话,需满足: f ( j ) < = n − j + 1 f(j) <= n - j + 1 f(j)<=nj+1
    转移过来就是 f ( j ) − n + j − 1 f(j) - n + j - 1 f(j)n+j1,表示的就是至少需要增加的行数
    答案就是: max ⁡ 1 < = j < = p o s m a x f ( j ) − n + j − 1 \max\limits_{1<=j<=pos_{max}} f(j) - n + j - 1 1<=j<=posmaxmaxf(j)n+j1
    因此每次增加或删除一颗棋子,更新前缀 + 1 +1 +1 − 1 -1 1 即可
    用线段树维护,初始值为 j − 1 j-1 j1,最后 − n -n n
     p o s m a x pos_{max} posmax表示出现的棋子里最大的行

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 4e5 + 5;
int n, m, k, t[maxn<<2];
int lz[maxn<<2], cnt[maxn];
 
void build(int l, int r, int rt) {
	if(l == r) {
		t[rt] = l - 1;
		return;
	}
	int mid = l + r >> 1;
	build(l, mid, rt<<1);
	build(mid+1, r, rt<<1|1);
	t[rt] = max(t[rt<<1], t[rt<<1|1]);
}
 
void pushdown(int rt) {
	if(lz[rt] != 0) {
		t[rt<<1] += lz[rt], t[rt<<1|1] += lz[rt];
		lz[rt<<1] += lz[rt], lz[rt<<1|1] += lz[rt];
		lz[rt] = 0;
	}
}
 
void update(int L, int R, int c, int l, int r, int rt) {
	if(l>R || r<L) return;
	if(l>=L && r<=R) {
		t[rt] += c, lz[rt] += c;
		return;
	}
	pushdown(rt);
	int mid = l + r >> 1;
	update(L, R, c, l, mid, rt<<1);
	update(L, R, c, mid+1, r, rt<<1|1);
	t[rt] = max(t[rt<<1], t[rt<<1|1]);
}
 
int query(int L, int R, int l, int r, int rt) {
	if(l>R || r<L) return 0;
	if(l>=L && r<=R) return t[rt];
	pushdown(rt);
	int mid = l + r >> 1, ret = 0;
	ret = max(ret, query(L, R, l, mid, rt<<1));
	ret = max(ret, query(L, R, mid+1, r, rt<<1|1));
	t[rt] = max(t[rt<<1], t[rt<<1|1]);
	return ret;
}
 
signed main() {
	scanf("%d%d%d", &n, &k, &m);
	build(1, n<<1, 1);
	set <pii> p;
	set <int> mx;
	while(m--) {
		int x, y;
		scanf("%d%d", &y, &x);
		int pos = x + abs(y - k);
		if(p.count({x, y})) {
			cnt[pos]--;
			if(cnt[pos] == 0) mx.erase(pos);
			p.erase({x, y});
			update(1, pos, -1, 1, n<<1, 1);
		} else {
			cnt[pos]++;
			mx.insert(pos);
			p.insert({x, y});
			update(1, pos, 1, 1, n<<1, 1);
		}
		if(mx.empty()) puts("0");
		else printf("%d\n", max(query(1, *mx.rbegin(), 1, n<<1, 1) - n, 0));
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值