题目链接
题意
有一个长度为的数组,另外给出段区间, 你知道这段区间内的所有元素的和, 给出区间的描述为,保证所有的区间都合法, 并且对任何
问给出的这些区间能不能确定这个元素的和
数据范围
题解
显然我们可以用这些区间对整个数组进行覆盖, 看是否可以每个位置都只被覆盖一次
覆盖操作分为三种
- 将区间内所有数的覆盖次数加1
- 将区间内所有数的覆盖次数减1
- 什么也不做, 相当于这个区间没有用
思路
那么我们就可以对于每个位置, 找到所有可以覆盖到这个位置的区间, 然后决定用哪些区间覆盖使得这个位置只被覆盖1次
这显然是无法实现的, 因为你不能确定对每个位置用那些区间怎么覆盖, 不同位置如果被同一个区间包含那也不好处理
优化
如果我们将区间按左端点为关键字从小到大排序, 这样的话我们就可以从到顺序枚举每个位置, 如果这个位置目前被覆盖的次数需要被修改, 那我们就看看有没有对应左端点为这个位置的区间并尝试更改
但是这还是有问题的, 因为可能出现一个位置对应了多个区间的左端点的情况, 这样我们还是不能确定怎么覆盖
继续优化
既然可能出现一个位置对应了多个区间左端点的情况, 那我们就通过分割把他们分成左端点不同的两段区间
假设存在, , 那么我们可以把他们分成两段左端点不同的等效区间
这样就不存在左端点相同的情况了, 然后顺序枚举每个位置即可
代码
实现思路
在实际写代码的过程中, 我们将区间的左端点作为第一关键字, 右端点作为第二关键字从小到大排序, 然后将左端点相同的区间分割, 为实现这个操作, 我们只需要在排好序的区间序列中把当前区间的左端点与前一个区间的左端点比较就好了
但是可能会存在如下情况:
, 我们将其分割成后, 又出现了存在为左端点重复的情况,那我们就需要用来更新其他区间, 所以我们的区间序列实际上应该是动态维护的, 鉴于完全相同的两个区间显然有一个就行, 我们使用stl set
来维护这个序列
如果原始区间存在多个区间共享一个左端点的情况, 这个方法也是可以处理的, 精髓就在于将分割出的区间塞回set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include <iostream> #include <cstdio> #include <set>
int n, q; int s[200005]; int mxr[200005]; struct Seg { int a, b;
bool operator < (Seg tmp) const { if(a < tmp.a) return true; else if(a > tmp.a) return false; else return b < tmp.b; } }; std:: set<Seg> st; int l[200005], r[200005], cnt;
int main() { scanf("%d%d", &n, &q); for(int i = 1; i <= q; i ++) { int a, b; scanf("%d%d", &a, &b);
st.insert((Seg){a, b}); }
while(! st.empty()) { int a = st.begin()->a, b = st.begin()->b; st.erase(st.begin());
if(mxr[a]) st.insert((Seg){mxr[a] + 1, b}); else cnt ++, l[cnt] = a, r[cnt] = b; mxr[a] = b; }
bool judge = true;
int now = 0; for(int i = 1; i <= n; i ++) { s[i] += s[i - 1];
if(i == l[now + 1]) now ++;
if(s[i] == 1) continue; else if(s[i] == 2) { if(i == l[now]) s[i] --, s[r[now] + 1] ++; else judge = false; } else if(s[i] == 0) { if(i == l[now]) s[i] ++, s[r[now] + 1] --; else judge = false; } else judge = false; }
if(judge) printf("Yes\n"); else printf("No\n");
return 0; }
|
表示以为左端点的区间中(不论这个区间是原始区间还是最终区间, 都要参与数组的更新), 最大的右端点, 保证了分割出的区间不会有冗余
问题
我们分割出了一段新的区间, 会不会存在区间且已经被记录到最终更新区间了?
如果存在, 那么我们这个做了显然就是错误的, 但是事实是不会存在, 因为分割出说明我们当前处理到的才到, 显然, 所以不会出现这种情况