E. Tracking Segments(二分)

文章描述了一个编程挑战,其中涉及处理一个全0数组和多个段,判断在对数组元素进行特定次数的值设为1的操作后,是否有段变得漂亮(1的数量多于0)。文章提出了二分查找和前缀和的方法来优化解决方案,并给出了输入输出示例以及一个C++代码实现。
摘要由CSDN通过智能技术生成


Problem - 1843E - Codeforcesicon-default.png?t=N5K3https://codeforces.com/problemset/problem/1843/E

题目:

给你一个由n个零组成的数组a。也给你一组m个不一定不同的段。每个段由两个数字L,R 定义; (1 <= L <= R <= n)表示数组a的子数组[aL , aL+1,......,aR]

如果该线段上1的个数严格大于0的个数,则是漂亮的。比如a = [1,0,1,0,1],那么段[1,5]是漂亮的(1的个数是3,0的个数是2),但段[3,4]不是漂亮的(1的个数是1,0的个数是1)。 

你有q次变化的操作。对于每一个变化,你被赋予一个数字1 <= x <= n,这意味着你必须给元素ax赋值1。你必须找到第一个变化,使得在这之后,m个给定的线段中至少有一个变得漂亮,或者在处理完所有q个变化之后,报告它们都不漂亮。 

input:

第一行包含一个整数t(1≤t≤10^4)——测试用例的数量。

每个测试用例的第一行包含两个整数n和m(1 <= m <= n <= 10^5)——分别是数组a的大小和段的数量。 然后有m条线,由两个数l,r组成(1 <= l <= r <=  n)片段的边界。 

下一行包含一个整数q(1<= q <=n)——变化的次数。 接下来的q行每一行都包含一个整数x(1 <= x <= n)——需要设置为1的数组元素的索引。保证查询中的索引是不同的。 保证所有测试用例的n之和不超过10^5。

output:

对于每个测试用例,输出一个整数——最小变化数,在此之后至少有一个线段是美丽的,或者如果没有线段是美丽的,则输出-1。

测试用例:

input
6
5 5
1 2
4 5
1 5
1 3
2 4
5
5
3
1
2
4
4 2
1 1
4 4
2
2
3
5 2
1 5
1 5
4
2
1
3
4
5 2
1 5
1 3
5
4
1
2
3
5
5 5
1 5
1 5
1 5
1 5
1 4
3
1
4
3
3 2
2 2
1 3
3
2
3
1
output
3
-1
3
3
3
1

思路

因为片段是可以重复的,所以我们可以用set来保存片段去避免重复。

需要找到第一个开始之后都是漂亮的变化。如果直接暴力操作的话,很容易超时。所以就使用二分查找+前缀和的方式进行处理。

先将x[]保存下来,再对其进行二分查找,找到最早的符合要求的变化。checked函数对x数组进行遍历赋值1的操作,再遍历试图查找到符合要求的情况返回1 。

代码

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;


set<pair<int, int>>s;
int a[N];
int x[N];//保存q次变化
int sum[N];//前缀和
int n, m;
bool checked(int mid) {
	for (int i = 1;i <= n;i++) a[i] = 0;
	for (int i = 1;i <= mid;i++) a[x[i]] = 1;
	for (int i = 1;i <= n;i++) sum[i] = sum[i - 1] + a[i];
	for (auto i : s) {
		if (sum[i.second] - sum[i.first - 1] > (i.second - i.first + 1) / 2)return 1;
	}
	return 0;
}
int main() {
	int t;
	cin >> t;
	while (t--) {
		s.clear();
		cin >> n >> m;
		for (int i = 1;i <= m;i++) {
			int l, r;
			cin >> l >> r;
			s.insert({ l,r });
		}
		int q;
		cin >> q;
		for (int i = 1;i <= q;i++) {
			cin >> x[i];
		}
		int l = 1, r = q, mid;
		int ans = -1;
		while (l <= r) {
			mid = (l + r) / 2;
			//cout << l << " " << r << " " << mid << endl;
			if (checked(mid)) {
				ans = mid;
				r = mid - 1;
			}
			else {
				l = mid + 1;
			}
		}
		cout << ans << endl;
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值