Educational Codeforces Round 172 (Rated for Div. 2) D题题解记录

大致题意:给定一个坐标轴,从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 risj,然后找到所有坐标轴的点 x x x的个数,满足如下条件:对于 ∀ s j ⊆ s \forall s_j\subseteq s sjs x ∈ s j ∩ x ∉ r i x\in s_j \cap x\notin r_i xsjx/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;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值