​2276. 统计区间中的整数数目​​(set&动态开点)

1.set合并

排序按照右端点从小到大,右端点相同左端点从小到大排序。

然后用一个cnt遍历维护答案。

每个区间最多被插入删除一次。

所以时间复杂度:

class CountIntervals {
typedef pair<int, int> pii;

int ans = 0;
set<pii> st;

public:
CountIntervals() {
}

void add(int left, int right) {
int L = left, R = right;
// 这里 (R1, L1) >= (R2, L2),若 R1 > R2 或 R1 = R2 且 L1 >= L2
auto it = st.lower_bound(pii(left - 1, -2e9));
while (it != st.end()) {
if (it->second > right + 1) break;
L = min(L, it->second);
R = max(R, it->first);
ans -= it->first - it->second + 1;
st.erase(it++);
}
ans += R - L + 1;
st.insert(pii(R, L));
}

int count() {
return ans;
}
};

2.动态开点线段树

每个结点存对应的端点范围和对应的覆盖的个数。

class CountIntervals {
CountIntervals *left = nullptr, *right = nullptr;
int l, r, cnt = 0;

public:
CountIntervals() : l(1), r(1e9) {}

CountIntervals(int l, int r) : l(l), r(r) {}

void add(int L, int R) { // 为方便区分变量名,将递归中始终不变的入参改为大写(视作常量)
if (cnt == r - l + 1) return; // 当前节点已被完整覆盖,无需执行任何操作
if (L <= l && r <= R) { // 当前节点已被区间 [L,R] 完整覆盖,不再继续递归
cnt = r - l + 1;
return;
}
int mid = (l + r) / 2;
if (left == nullptr) left = new CountIntervals(l, mid); // 动态开点
if (right == nullptr) right = new CountIntervals(mid + 1, r); // 动态开点
if (L <= mid) left->add(L, R);
if (mid < R) right->add(L, R);
cnt = left->cnt + right->cnt;
}

int count() { return cnt; }
};