题目:
现有n个黑色石子。每次将(l,r)内的石子染成黑色或白色。每次染色后输出当前有多少个黑色石子。
(1 ≤ n ≤ 1e9, 1 ≤ q ≤ 3·1e5)
分析:
这道题也是用ODT的思想,合并和拆开区间。
分析可得:虽然n非常大。但一开始只有一个三元组,每次操作最多增加两个三元组,每个三元组最多删除一次。所以set中元素不会特别多。而黑色石子的和可以随着操作一起维护。
用set来维护黑色石子的区间,一开始是(1,n)。因为set中不包括白色石子的区间,所以set中的区间不一定连续,需要修改一下split函数,使得其能正确返回想要的区间。
split(int pos)的功能是:返回一个区间(L,R),其中 L 要大于等于 pos。
这样假如我们要把(l,r)涂成白色,就在set中找到包含在(l,r)这段区间内的黑色区间,然后把他们全部删除,维护sum即可。如果是涂成黑色,同样先全部删除,然后再添加进一个(L,R)即可。
通过这道题发现,pair的速度比struct快很多很多。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 105;
const double EPS = 1e-8;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> P;
set<P> s;
int n, m, sum;
set<P>::iterator split(int pos) {
if(s.empty()) return s.end();
auto it = s.lower_bound(make_pair(pos,0));
if(it != s.end() && it->first == pos) return it;
if(it == s.begin()) return it;
auto last = --it; ++it;
if (it!=s.end() && it->first > pos && last->second < pos) return it;
--it;
if (pos > it->second) return s.end();
int L = it->first, R = it->second;
s.erase(it);
s.insert(make_pair(L, pos - 1));
return s.insert(make_pair(pos, R)).first;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
sum = n;
s.insert(make_pair(1,n));
for (int i = 1; i <= m; i++) {
int l,r,k;
cin >> l >> r >> k;
split(l);
auto itr = split(r + 1), itl = split(l);
while(itl != itr){
auto tmp = itl;
sum -= (tmp->second-tmp->first+1);
itl++;
s.erase(tmp);
}
if(k==2){
s.insert(make_pair(l,r));
sum += (r-l+1);
}
cout << sum << endl;
}
return 0;
}