CF1843E 二分+前缀和

发布时间 2023-06-27 12:17:00作者: LegendN

题意:

给定一个长度为n且均为0的数组,q次单点修改(从0改为1),以及m个基于该数组的区间。

规定好区间为:区间内1的个数严格大于0的个数。

上述m个区间若存在一个好区间则为合法,问按顺序进行q次单点修改过程中最早出现合法的单次修改编号,若无则输出-1。

马后炮思考:

对于m个区间,其实际关系是无序的。二分最重要的是考虑单调性,本题中单点修改的不断积累过程本质是趋于合法的单调。因而考虑二分q,一个log。

那么对于某一次二分的check(x),对前x个单点修改作前缀和cnt统计,O(n)。随后遍历所有的m个区间,通过查看cnt[R] - cnt[L-1]判断是否为好区间,得出合法性。

下面是代码,若有误,请您指出。

 1 #include<iostream>
 2 #include<vector> 
 3 #define ll long long
 4 using namespace std;
 5 
 6 const int N = 1e5 + 10;
 7 int L[N], R[N], q[N];
 8 int cnt[N];
 9 int n, m;
10 
11 bool check(int x)
12 {
13     for(int i = 0 ; i <= n ; i++) {
14         cnt[i] = 0;
15     }
16     for(int i = 1 ; i <= x ; i++) {
17         cnt[q[i]] = 1;
18     }
19     for(int i = 1 ; i <= n ; i++) {
20         cnt[i] += cnt[i - 1];
21     }
22     for(int i = 1 ; i <= m ; i++) {
23         int len = R[i] - L[i] + 1;
24         if(cnt[R[i]] - cnt[L[i] - 1] > len / 2){
25             return true;
26         }
27     }
28     return false;
29 }
30 
31 int main(){
32     ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
33     int cas; cin >> cas;
34     while(cas--)
35     {
36         cin >> n >> m;
37         for(int i = 1 ; i <= m ; i++) {
38             cin >> L[i] >> R[i];
39         }
40         int Q; cin >> Q;
41         for(int i = 1 ; i <= Q ; i++) {
42             cin >> q[i];
43         }
44         int l = 1, r = Q;
45         while(l < r) {
46             int mid = (l + r) >> 1;
47             if(check(mid)) {
48                 r = mid;
49             }else {
50                 l = mid + 1;
51             }
52         }
53         if(check(l) && l <= Q) {
54             cout << l << endl;
55         }else if(check(l + 1) && l + 1 <= Q){
56             cout << l + 1 << endl;
57         }else {
58             cout << -1 << endl;
59         }
60     }
61     
62     return 0;
63 }