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]\)。
考虑两块布的组合方式:
-
其中一块布就是 \([S, T]\),另一块布完全包含在 \([S, T]\) 内。
此时并集仍然是 \([S, T]\)。 -
两块布分别覆盖 \([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;
}

浙公网安备 33010602011771号