Problem - 1843E - Codeforceshttps://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;
}
}