Crossing Table Cloth

Crossing Table Cloth

区间维护问题

题意

有 \(N\) 个单元格排成一行,\(M\) 块布,第 \(i\) 块布覆盖区间 \([L_i, R_i]\)。
有 \(Q\) 个询问,每个询问给定 \(S, T\),判断能否恰好选择两块布铺设,使得:

  • 单元格 \(S\) 到 \(T\) 全部被覆盖;
  • 其他单元格都没有被覆盖。

即两块布的并集恰好为 \([S, T]\)。

关键观察

两块布的并集要恰好等于 \([S, T]\),且不能覆盖区间外的单元格。
因此两块布必须完全落在 \([S, T]\) 内,并且它们的并集覆盖整个 \([S, T]\)。

考虑两块布的组合方式:

  1. 其中一块布就是 \([S, T]\),另一块布完全包含在 \([S, T]\) 内。
    此时并集仍然是 \([S, T]\)。

  2. 两块布分别覆盖 \([S, x]\) 和 \([y, T]\),其中 \(x \ge y - 1\)。
    即一块以 \(S\) 为左端点,另一块以 \(T\) 为右端点,中间相邻或重叠,且没有超出 \([S, T]\)。

因此,对于每个询问 \((S, T)\),只需要判断是否存在上述两种情况的布组合。

算法流程

维护以下信息:

  • left[l]:所有左端点为 \(l\) 的布的右端点集合。
  • right[r]:所有右端点为 \(r\) 的布的左端点集合。
  • val[l][r]:区间 \([l, r]\) 出现的次数。
  • 树状数组(Fenwick)维护:对于每个位置 \(i\),以 \(i\) 为左端点的最小右端点。
    用于快速查询某个区间内是否存在完全包含的子区间。

对于每个询问 \((S, T)\):

情况一:存在布 \([S, T]\)

  • 如果 val[S][T] >= 2,直接选两块 \([S, T]\),输出 Yes。
  • 否则,需要另一块布完全包含在 \([S, T]\) 内。
    通过树状数组查询:
    • bit.query(S, T) < T,或
    • bit.query(S + 1, T) <= T
      若满足任一条件,说明存在完全包含的子区间,输出 Yes,否则 No。

情况二:不存在布 \([S, T]\)

  • 必须选择一块以 \(S\) 为左端点的布,和一块以 \(T\) 为右端点的布。
  • 对于以 \(S\) 为左端点的布,贪心地选择右端点尽可能大但不超过 \(T\) 的布,设其右端点为 \(r\)。
  • 对于以 \(T\) 为右端点的布,贪心地选择左端点尽可能小但不小于 \(S\) 的布,设其左端点为 \(l\)。
  • 如果这两块布存在,并且 \(r \ge l - 1\),则输出 Yes,否则 No。

数据结构

  • set<int>:存储每个左端点对应的右端点集合,以及每个右端点对应的左端点集合。
    利用 lower_bound 快速查找满足条件的区间。
  • map<int, int>:统计相同区间的出现次数。
  • 树状数组(Fenwick):支持区间最小值查询。
    每个位置 \(i\) 存储以 \(i\) 为左端点的最小右端点。
    查询区间 \([L, R]\) 的最小值,判断是否存在一个区间完全包含在 \([L, R]\) 内。

复杂度分析

  • 预处理:\(O((N + M) \log N)\)
  • 每次查询:\(O(\log N)\)
  • 总时间复杂度:\(O((N + M + Q) \log N)\)
  • 空间复杂度:\(O(N + M)\)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using tiii = tuple<int, int, int>;
using vvi = vector<vector<int>>;
const int INF = 1e8;

struct Fenwick {
    int n;
    vector<int> tree, a;

    Fenwick(int n = 0) : n(n), tree(n + 1, INF), a(n + 1, INF) {}

    void build(const vector<int>& arr) {
        for (int i = 1; i <= n; ++i) {
            a[i] = tree[i] = arr[i];
        }
        for (int i = 1; i <= n; ++i) {
            int j = i + (i & -i);
            if (j <= n) tree[j] = min(tree[j], tree[i]);
        }
    }

    int query(int l, int r) const {
        int res = INF;
        int i = r;
        while(i >= l) {
            if(i - (i & -i) + 1 >= l) {
                res = min(res, tree[i]);
                i -= i & -i;
            }
            else {
                res = min(res, a[i]);
                i --;
            }
        }
        return res;
    }
};

void solve() {
    int  n, m, q; cin >>n >> m;
    vector<set<int>> left(n+1), right(n + 1);
    vector<map<int, int>> val(n + 1);
    for(int i = 1; i <= m; i ++) {
        int l, r; cin >> l >> r;
        left[l].insert(r);
        right[r].insert(l);
        val[l][r] ++;
    }
    vector<int> a(n + 1, INF);

    for(int i = 1; i <= n; i ++) {
        if(!left[i].empty()) a[i] = *left[i].begin();
    }
    Fenwick bit(n); bit.build(a);
    cin >> q;
    while(q --) {
        int s, t; cin >> s >> t;
        if(val[s].count(t)) {
            if(val[s][t] >= 2) cout << "Yes\n";
            else if(bit.query(s, t) < t || bit.query(s + 1, t) <= t) cout << "Yes\n";
            else cout << "No\n";
        }
        else {
            if(left[s].empty() || right[t].empty()) {cout << "No\n"; continue;}
            auto it = left[s].lower_bound(t);
            auto it2 = right[t].lower_bound(s);
            if(it == left[s].begin() || it2 == right[t].end() || *it2 > t) cout << "No\n";
            else {
                it --;
                int l = *it, r = *it2;
                if(l >= r - 1) cout << "Yes\n";
                else cout << "No\n";
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int _=1; //cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-10-03 09:55  thenrify  阅读(7)  评论(0)    收藏  举报