• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【区间并集】Atcoder 457 E - Crossing Table Cloth

题目

https://atcoder.jp/contests/abc457/tasks/abc457_e

题意

第一行输入两个正整数 \(n, m(1 \leq n \leq 2 \times 10^5, 2 \leq m \leq 2 \times 10^5)\);
接下来 \(m\) 行,每行输入两个正整数 \(l_i, r_i(1 \leq l_i \leq r_i \leq n)\),代表一个区间 \([l_i,r_i]\);
第 \(m+2\) 行输入一个正整数 \(q\),代表有 \(q\) 次询问,对于每次询问:
输入两个正整数 \(s_j,t_j\),问是否存在至少一种方案,恰好从 \(m\) 个区间中选择 \(2\) 个区间,这 \(2\) 个区间的并集恰好等于 \([s_j,t_j]\),若存在则输出 \(Yes\),否则输出 \(No\)。

题解

这道题看似人畜无害,实际危机重重。笔者算是深受荼毒,wa 了无数发,特来写下此文记录应对方案。

两个不同的集合 \(U_x\) 和 \(U_y\),不妨假设满足 \(U_x\) 的左端点小于等于集合 \(U_y\) 的左端点,若两个集合的左端点相同,则集合 \(U_x\) 的右端点大于等于集合 \(U_y\) 的右端点。要使用集合 \(U_x\) 与 集合 \(U_y\) 求并集,且并集恰好为 \([s_j,t_j]\),那么对于集合 \(U_x\) 与 集合 \(U_y\) 需要满足以下条件之一:

  1. 集合 \(U_x\) 恰好为 \([s_j,t_j]\),且集合 \(U_y \subseteq U_x\);
  2. 集合 \(U_x\) 的左端点恰好为 \(s_j\),集合 \(U_y\) 的右端点恰好为 \(t_j\),且集合 \(U_x\) 的右端点大于等于集合 \(U_y\) 的左端点 + 1。

对于条件 1,可以使用贪心算法维护。对所有集合按首先左端点越小越优先,其次右端点越大越优先的规则排序,可以确保一个集合若有子集,则其真子集必定在自己之后的位置。

对于条件 2,若满足条件 1 则无需判断,否则找到一个左端点为 \(s_j\) 的区间,且让其右端点在不大于 \(t_j\) 的情况下尽量大,再找一个右端点为 \(t_j\) 的区间,且让其左端点再不小于 \(s_j\) 的情况下尽量小。具体的代码维护,可以使用二分法实现。

参考代码

#include<bits/stdc++.h>
#include<ranges>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;

int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);
    int n, m, l, r, q;  cin >> n >> m;
    vector<vector<int>> a(n + 1), b(n + 1);
    vector<PII> v(m);
    for (int i = 0; i < m; ++ i) {
        cin >> l >> r;
        v[i].first = l, v[i].second = r;
        a[l].push_back(r);// 维护左端点为 l 的区间,右端点有哪些
        b[r].push_back(l);// 维护右端点为 r 的区间,左端点有哪些
    }
    // 对所有集合按首先左端点越小越优先,其次右端点越大越优先的规则排序,确保一个集合若有子集,则其真子集必定在自己之后的位置
    ranges::sort(v, [&](PII& p1, PII& p2) {
        return p1.first != p2.first ? p1.first < p2.first : p2.second < p1.second;
    });
    set<ll> st;// 维护哪些集合有子集
    for (int i = m - 1/*当前下标位置*/, j = n + 1; i >= 0; -- i) {
        l = v[i].first, r = v[i].second;
        if (j <= r) st.insert(1LL * l * n + r);// 本质是记录集合 [l, r] 有子集
        j = min(j, r);// 维护已遍历过的集合的最小右端点
    }
    for (int i = 1; i <= n; ++ i) ranges::sort(a[i]), ranges::sort(b[i]);
    cin >> q;
    while (q --) {
        cin >> l >> r;
        if (st.find(1LL * l * n + r) != st.end()) {// 集合 [l, r] 存在子集,必定满足
            cout << "Yes\n";
            continue;
        }
        // 若区间不合法,直接返回 No:
        //(1)$l > r$ 显然不合法,不过题目有保证不存在该情况
        // (2) 同时只存在一个区间左边界为 $l$ 和只存在一个区间右边界为 $r$,并且这个区间是 $[l, r]$
        if (l > r || a[l].empty() || b[r].empty() || (a[l].size() == 1 && a[l][0] == r && a[r].size() == 1)) {
            cout << "No\n";
            continue;
        }
        auto it1 = upper_bound(a[l].begin(), a[l].end(), r);// 求出以 l 为左边界,右边界大于 r 的最小迭代器
        bool flag = false;// 初始时标记不存在合法情况
        if (it1 != a[l].begin()) {// it1 不是第一个迭代器,说明有前驱节点,即存在以 l 为左边界且右边界小于等于 r 的区间
            -- it1;// 向前移动一个迭代器
            auto it2 = lower_bound(b[r].begin(), b[r].end(), l);// 求出以 r 为右边界,左边界大于等于 l 的最小迭代器
            flag = it2 != b[r].end() && (*it1 < r || *it2 > l) && *it1 >= *it2 - 1;// it2 没越界代表区间存在,若俩区间都含于 [sj,tj] 且两个区间存在非空交集,则说明并集就是 [sj,tj]
            if (it2 != b[r].end()) {// it2  指向的迭代器还没达到数组 b 的越界位置
                if (it1 != a[l].begin()) {// it1 指向的迭代器仍然有前驱动节点
                    auto it3 = prev(it1);// it3 指向 it1 的前驱节点的迭代器
                    if (*it3 >= l && *it2 <= r && *it3 >= *it2 - 1) flag = true;// 将区间 Ux 的右端点缩小,若仍满足 Ux 的右端点大于等于 Uy 的左端点 - 1,则说明存在两个不同的区间的并集就是 [sj,tj]
                }
                auto it4 = next(it2);// it4 指向 it2 的后继节点
                if (it4 != b[r].end() && *it1 >= l && *it4 <= r && *it1 >= *it4 - 1) flag = true;// it4 指向的迭代器还没达到数组 b 的越界位置,若仍满足 Ux 的右端点大于等于 Uy 的左端点 - 1,则说明存在两个不同的区间的并集就是 [sj,tj]
            }
        }
        cout << (flag ? "Yes\n" : "No\n");
    }
    return 0;
}

posted on 2026-06-26 01:00  RomanLin  阅读(3)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3