CF 807 E. Mark and Professor Koro(权值线段树)

Problem - E - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/contest/1705/problem/E给你n个数的序列,再给你m个操作,每次操作给出你两个数k,x

将第k个数改为x,并问你改完之后可以选择出现2次的数合并为该数+1的数,求完成这些操作后序列的最大值。

假设某一个数是x,若有出现两次以上的x,那么我们就可以合并为x+1,可以想到二进制加法,当一个二进制数为00011,若第一位+1,则该数变为00100,跟我们上面的情况一样,所以我们可以将其转化为二进制加法,然后用线段树维护。

const int N = 2e5 + 106;
struct node {
	int l, r;
	int add;
	int sum;
} tr[MAXN * 3];
int cnt[MAXN], a[MAXN];

void push_up(int q) {
	tr[q].sum = tr[q << 1].sum + tr[q << 1 | 1].sum;
}

void update(int q, int add) {
	tr[q].sum += (tr[q].r - tr[q].l + 1) * add;
	tr[q].add += add;
}

void push_down(int q) {
	if (tr[q].add) {
		update(q << 1, tr[q].add), update(q << 1 | 1, tr[q].add);
		tr[q].add = 0;
	}
}

void build(int q, int l, int r) {
	tr[q].l = l, tr[q].r = r;
	if (l == r)
		return void(tr[q].sum = cnt[r]);
	int mid = l + r >> 1;
	build(q << 1, l, mid);
	build(q << 1 | 1, mid + 1, r);
	push_up(q);
}

void modify(int q, int l, int r, int add) {
//	printf("2\n");
	if (tr[q].l >= l && tr[q].r <= r) {
		update(q, add);
		return;
	}
	push_down(q);
	int mid = tr[q].l + tr[q].r >> 1;
	if (l <= mid)
		modify(q << 1, l, r, add);
	if (r > mid)
		modify(q << 1 | 1, l, r, add);
	push_up(q);
}

int query0(int q, int l) {
//	printf("3\n");
	if (tr[q].r < l || tr[q].sum == tr[q].r - tr[q].l + 1)
		return -1;
	if (tr[q].l == tr[q].r)
		return tr[q].r;
	push_down(q);
	int t = query0(q << 1, l);
	if (~t)
		return t;
	return query0(q << 1 | 1, l);
}

int query1(int q, int l) {
//	printf("4\n");
	if (tr[q].r < l || !tr[q].sum)
		return -1;
	if (tr[q].l == tr[q].r)
		return tr[q].r;
	push_down(q);
	int t = query1(q << 1, l);
	if (~t)
		return t;
	return query1(q << 1 | 1, l);
}

int querymax(int q) {
//	printf("5\n");
	if (tr[q].l == tr[q].r)
		return tr[q].r;
	push_down(q);
	if (tr[q << 1 | 1].sum)
		return querymax(q << 1 | 1);
	return querymax(q << 1);
}

int query(int q, int x) {
	if (tr[q].l == tr[q].r)
		return tr[q].r;
	push_down(q);
	int mid = tr[q].l + tr[q].r >> 1;
	if (x <= mid)
		return query(q << 1, x);
	return query(q << 1 | 1, x);
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		cnt[a[i]]++;
	}
	for (int i = 1; i <= N; i++) {
		cnt[i + 1] += cnt[i] / 2;
		cnt[i] %= 2;
	}
	build(1, 1, N);
	while (m--) {
		int k, x;
		cin >> k >> x;
		int pos = query1(1, a[k]);
		modify(1, pos, pos, -1);
		if (pos != a[k])
			modify(1, a[k], pos - 1, 1);
		a[k] = x;
//		printf("===\n");
		pos = query0(1, a[k]);
		modify(1, pos, pos, 1);
		if (pos != a[k])
			modify(1, a[k], pos - 1, -1);
		cout << querymax(1) << '\n';
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值