大致题意:给定一个坐标轴,从1到1e9。现在给出n个区间 r i = r_i= ri= ( L i , R i ) (L_i,R_i) (Li,Ri)。对于每个区间 r i r_i ri,都需要查询这样的信息:首先找到所有的区间构成集合 s s s,这些区间 s j s_j sj满足条件: r i ⊆ s j r_i\subseteq s_j ri⊆sj,然后找到所有坐标轴的点 x x x的个数,满足如下条件:对于 ∀ s j ⊆ s \forall s_j\subseteq s ∀sj⊆s, x ∈ s j ∩ x ∉ r i x\in s_j \cap x\notin r_i x∈sj∩x∈/ri。
解:
对于每个区间,其对应的
x
x
x点可以分成两部分,左边的和右边的。如果先前的区间的左端点都小于等于当前的区间的左端点,那么我我们只需要找到先前区间中大于等于当前区间右端点的最小右端点。画个图大概是这样:
按照
sort(a.begin() + 1, a.begin() + 1 + n, [&](state a, state b) {
if (a.l != b.l) return a.l < b.l;
else return a.r > b.r;
});
这种规则排序,对于所有已经检查过的区间,记录下他们的右区间。对于当前查询的区间,我们可以通过二分来找到离当前右区间最近的大于等于右区间的位置。这样我们就计算出来右区间中
x
x
x的个数了。
如何找到左区间的?其实左右区间有对称性,我们可以处理右区间的逻辑处理左区间。对于原先是
(
1
,
3
)
(1,3)
(1,3)的区间,我们转换成
(
−
3
,
−
1
)
(-3,-1)
(−3,−1),按照同样的逻辑再处理一遍,就可以保证在所谓左区间(实际上是右区间)都囊括的前提下寻找最近的大于等于的右区间(实际上是原来的左区间)。
code
struct state {
int l, r, idx;
};
void solve() {
int n;
cin >> n;
vector<state> a(n + 1);
vector<int> ans(n + 1);
map<pii, int> mp;
for (int i = 1; i <= n; i++) {
cin >> a[i].l >> a[i].r;
a[i].idx = i;
mp[{a[i].l, a[i].r}]++;
mp[{-a[i].r, -a[i].l}]++;
}
for (int ii = 1; ii <= 2; ii++) {
sort(a.begin() + 1, a.begin() + 1 + n, [&](state a, state b) {
if (a.l != b.l) return a.l < b.l;
else return a.r > b.r;
});
set<int> rbd;
for (int i = 1; i <= n; i++) {
int temp = 0;
auto it = rbd.lower_bound(a[i].r);
if (i == 1) {
rbd.insert(a[i].r);
ans[a[i].idx] = 0;
continue;
}
if (mp[{a[i].l, a[i].r}] > 1) {
ans[a[i].idx] = 0;
rbd.insert(a[i].r);
continue;
}
if (it != rbd.end()) {
temp += *it - a[i].r;
}
rbd.insert(a[i].r);
ans[a[i].idx] += temp;
}
for (int i = 1; i <= n; i++) {
a[i].l = -a[i].l;
a[i].r = -a[i].r;
swap(a[i].l, a[i].r);
}
}
for (int i = 1; i <= n; i++) cout << ans[i] << endl;
}