题解:QOJ8035 Call Me Call Me
题意
有 \(n\) 个人,第 \(i\) 个人会决定参加会议,当且仅当有至少 \(k_i\) 个编号在 \([l_i,r_i]\) 中的人已经决定了参加会议。求参加会议的最大可能人数。\(1\leq n\leq 4\times 10^5\)。
题解
考虑一个暴力:用队列维护当前 \(k_i=0\) 的 \(i\),每次从队列中取出 \(i\),然后对于每个 \(l_j\leq i\leq r_j\) 的 \(j\),令 \(k_j\gets k_j -1\),若 \(k_j=0\) 则将 \(j\) 也加入队列中。时间复杂度为 \(\mathcal{O}(n^2)\)。
做一步优化,开一棵线段树,每个节点维护一个 vector,将 \(k_i>0\) 的区间在线段树上拆分成 \(\mathcal{O}(\log{n})\) 个节点,把 \(i\) 加入这些节点的 vector 中。每次从队列中取出 \(k_i=0\) 的 \(i\) 后,考虑线段树上每个包含 \(i\) 的节点,遍历其 vector 中的编号 \(j\) 并更新 \(k_j\),若 \(k_j=0\) 则将其加入队列中,并从 vector 中删除。需要注意的是,被删除的 \(j\) 仍然可能被包含在其他节点的 vector 中,遍历时遇到 \(k_j\leq 0\) 的 \(j\) 也需要及时删除。分析一下时间复杂度,每个 \(k_j>0\) 的 \(j\) 被遍历到时都会令 \(k_j\) 减小 \(1\),因此每个 \(j\) 最多被遍历 \(k_j+\log{n}\) 次,时间复杂度为 \(\mathcal{O}(n\log{n}+\sum k_i)\),还是可以被卡到 \(\mathcal{O}(n^2)\)。
考虑一个比较牛的优化,设阈值 \(B\),对于每一轮,我们取出当前所有 \(k_i=0\) 的 \(i\) 加入队列中,再取出所有 \(0<k_i\leq B\) 的 \(i\) 插入到线段树上,然后做上文所述操作直至队列为空,最后用前缀和更新 \(>B\) 的 \(i\) 对应的 \(k_i\),进入下一轮。分析一下时间复杂度,我们发现,若某一轮中对答案的贡献 \(<B\),则 \(>B\) 的 \(i\) 对应的 \(k_i\) 不可能被减成 \(0\),因此这种情况下我们可以直接终止操作。于是我们至多进行 \(\mathcal{O}(\dfrac{n}{B})\) 轮操作,时间复杂度为 \(\mathcal{O}(n\log{n}+nB+\dfrac{n^2}{B})\),取 \(B=\sqrt{n}\) 即可做到 \(\mathcal{O}(n\sqrt{n})\) 的时间复杂度。
实际写出来常数很小。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int N = 4e5 + 5;
template<typename T> inline T lowbit(T x) { return x & -x; }
template<typename T> inline void chk_min(T &x, T y) { if (y < x) x = y; }
template<typename T> inline void chk_max(T &x, T y) { if (x < y) x = y; }
int n, B, L[N], R[N], K[N], s[N];
bool vis[N];
queue<int> q;
struct SegTree {
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
vector<int> vec[N << 2];
void ins(int p, int l, int r, int x, int y, int id) {
if (x <= l && y >= r) return vec[p].emplace_back(id);
int mid = l + r >> 1;
if (x <= mid) ins(ls(p), l, mid, x, y, id);
if (y > mid) ins(rs(p), mid + 1, r, x, y, id);
}
void upd(int p, int l, int r, int x) {
for (int i = 0; i < vec[p].size(); ++i) {
int &id = vec[p][i];
if (!--K[id]) q.push(id);
if (K[id] <= 0) swap(id, vec[p].back()), vec[p].pop_back(), --i;
}
if (l == r) return;
int mid = l + r >> 1;
x <= mid ? upd(ls(p), l, mid, x) : upd(rs(p), mid + 1, r, x);
}
#undef ls
#undef rs
} sgt;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n, B = ceil(sqrt(n));
for (int i = 1; i <= n; ++i) cin >> L[i] >> R[i] >> K[i];
int tot = n;
while (tot) {
fill(s + 1, s + n + 1, 0);
for (int i = 1; i <= n; ++i) if (!vis[i] && K[i] <= B) {
!K[i] ? q.push(i) : sgt.ins(1, 1, n, L[i], R[i], i);
vis[i] = 1;
}
if (q.empty()) break;
while (!q.empty()) {
int x = q.front(); q.pop();
sgt.upd(1, 1, n, x), ++s[x], --tot;
}
for (int i = 1; i <= n; ++i) s[i] += s[i - 1];
for (int i = 1; i <= n; ++i) if (!vis[i]) K[i] = max(K[i] - s[R[i]] + s[L[i] - 1], 0);
}
cout << n - tot;
return 0;
}

浙公网安备 33010602011771号