题目链接:https://zoj.pintia.cn/problem-sets/91827364500/problems/1384062980244979712
官方题解:https://www.zhihu.com/question/455125989/answer/1840256385
思路
对于每个 (C imes C) 的点,其若被矩阵覆盖的话,必然存在一个最小覆盖编号 (min) 和最大覆盖编号 (max)。
那么对于每个点的 (min) 和 (max),若在询问的区间内 ([s,t]) 中,那么也就是说 (s leq min) 并且 (max leq t),那么其不会对答案产生贡献。
对于每个点,都能得到 ([min, max]),由于题目中有多次询问,考虑离线,对查询和得到的 ([min, max]) 区间按照 (r) 进行排序,扫到时将 (min) 对应的位置+1,查询时查询的区间内求和。看代码会更加好懂
关键在于怎么求得每个 (C imes C) 的点的最小覆盖编号 (min) 和最大覆盖编号 (max)。
考虑从左向右扫,能够能够将线段插入/删除,一开始我用set做线段树的 node,然后华丽地TLE了,问题在于,每次查询时复杂度会多一个 (log n),而题解中使用的优先队列能够使得查询复杂度为 (O(1))。
但是在ZOJ上跑了982ms,卡得飞起。
代码
#include <bits/stdc++.h>
#define pb push_back
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define SZ(x) (int)x.size()
#define pii pair<int, int>
#define mp make_pair
typedef long long ll;
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXC = 2005;
class BIT {
public:
int val[MAXN], n;
inline int lowbit(int x) {
return x & (-x);
}
inline void add(int pos, int v) {
for (int i = pos; i <= n; i += lowbit(i)) val[i] += v;
}
inline int _query(int pos) {
int ans = 0;
for (int i = pos; i >= 1; i -= lowbit(i)) ans += val[i];
return ans;
}
inline int query(int l, int r) {
return _query(r) - _query(l - 1);
}
} bit;
int minn[MAXC][MAXC], maxx[MAXC][MAXC];
class SEG {
public:
priority_queue<int> T1[MAXN << 2], tT1[MAXN << 2];
priority_queue<int, vector<int>, greater<int>> T2[MAXN << 2], tT2[MAXN << 2];
inline void add(int rt, int L, int R, int v, int be, int en) {
if (L <= be && en <= R) {
T1[rt].push(v), T2[rt].push(v);
return;
}
int mid = (be + en) >> 1;
if (L <= mid) add(rt << 1, L, R, v, be, mid);
if (R > mid) add(rt << 1 | 1, L, R, v, mid + 1, en);
}
inline void esc(int rt, int L, int R, int v, int be, int en) {
if (L <= be && en <= R) {
tT1[rt].push(v), tT2[rt].push(v);
return;
}
int mid = (be + en) >> 1;
if (L <= mid) esc(rt << 1, L, R, v, be, mid);
if (R > mid) esc(rt << 1 | 1, L, R, v, mid + 1, en);
}
inline void dfs(int rt, int C, int mx, int mi, int be, int en) {
while (!T1[rt].empty() && !tT1[rt].empty() && T1[rt].top() == tT1[rt].top()) T1[rt].pop(), tT1[rt].pop();
while (!T2[rt].empty() && !tT2[rt].empty() && T2[rt].top() == tT2[rt].top()) T2[rt].pop(), tT2[rt].pop();
if (!T1[rt].empty()) {
int t1 = T1[rt].top(), t2 = T2[rt].top();
if (t1 > mx) mx = t1;
if (t2 < mi) mi = t2;
// mx = max(mx, *(--T[rt].st.end())), mi = min(mi, *(T[rt].st.begin()));
}
if (be == en) {
maxx[C][be] = mx, minn[C][be] = mi;
return;
}
int mid = (be + en) >> 1;
dfs(rt << 1, C, mx, mi, be, mid), dfs(rt << 1 | 1, C, mx, mi, mid + 1, en);
}
} tree;
struct Q {
int l, r, id;
} qs[MAXN];
struct OPS {
int be, en, v, id;
};
vector<OPS> vec[MAXC];
vector<pii > vec2;
int res[MAXN];
int main() {
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
int xa, xb, ya, yb;
scanf("%d%d%d%d", &xa, &ya, &xb, &yb);
vec[xa + 1].pb({ya + 1, yb, 1, i});
vec[xb + 1].pb({ya + 1, yb, -1, i});
}
for (int i = 1; i <= 2000; i++) {
for (auto &e: vec[i]) {
if (e.v == 1) tree.add(1, e.be, e.en, e.id, 1, 2000);
else tree.esc(1, e.be, e.en, e.id, 1, 2000);
}
tree.dfs(1, i, -inf, inf, 1, 2000);
}
int sum = 0;
for (int i = 1; i <= 2000; i++) {
for (int j = 1; j <= 2000; j++) {
if (maxx[i][j] == -inf || minn[i][j] == inf) continue;
sum++;
vec2.pb(mp(minn[i][j], maxx[i][j]));
}
}
for (int i = 1; i <= q; i++) {
qs[i].id = i;
scanf("%d%d", &qs[i].l, &qs[i].r);
}
sort(qs + 1, qs + 1 + q, [&](const Q &ta, const Q &tb) {
if (ta.r != tb.r) return ta.r < tb.r;
else return ta.l < tb.l;
});
sort(vec2.begin(), vec2.end(), [&](const pii &ta, const pii &tb) {
return ta.second < tb.second;
});
bit.n = n;
int pos = 0;
for (int i = 1; i <= q; i++) {
int l = qs[i].l, r = qs[i].r;
while (pos < SZ(vec2) && vec2[pos].second <= r) {
bit.add(vec2[pos].first, 1);
pos++;
}
int idx = qs[i].id;
res[idx] = bit.query(l, r);
}
for (int i = 1; i <= q; i++) printf("%d
", sum - res[i]);
}
/*
3 6
1 1 4 4
2 2 5 5
3 3 6 6
*/