标签:algo bre lowbit 两种 back 怎么 ret while tree
题意:
有一个集合 \(S\),初始为空。进行 \(m\) 次操作哟,分下列两种:
- 修改:向集合内添加一个 \([1, n]\) 内的正整数 \(h\)。
- 定义 \(f(d)\) 为 \([1, d], [d + 1, 2d], [2d + 1, 3d], \ldots\) 这样取,第一次没有在集合内找到对应区间内元素的回数。询问:给出 \(l, r\),求 \(\sum \limits_{d = l} ^ r f(d)\)。
\(n \le 10 ^ 5\),\(m \le 10 ^ 6\)。
比赛时只会这题,自闭。
答案是调和级数 \(\mathcal{O}(n \log n)\) 级别的,考虑对每个 \(d\) 都用个 \(\texttt{std::set}\) 维护它的所有还没有覆盖集合内元素的区间,那么这个 \(d\) 对应的答案就是这些区间中最小的那个的编号,\(l, r\) 对应的答案直接树状数组求个区间和。
修改时,只要保证枚举到的每个区间都是还没有被覆盖的,这样均摊下去复杂度就是对的。难点在如何维护这个东西。
首先,跨越 \(h\) 的没有被覆盖的区间只和 \(h\) 在 \(S\) 中的前驱和后缀有关。分别记为 \(pre, nxt\)。
那么我们要找到的区间,设起点为 \(x\),长度为 \(l\)(\(l \mid x\)),大概是这样的:\(pre \le x \lt h \le x + l \lt nxt\)(也可以写作 \(pre \le x - l \lt h \le x \lt nxt\))。
我们在 \([pre, h)\) 和 \([h, nxt)\) 较短一侧内枚举起点 \(x\),然后二分找到 \(x\) 最早跨到另外一个区间的因子,不断删除这样的区间直到跨到区间之外。就完成了只枚举到了所有还未被覆盖的跨越 \(h\) 的区间,维护直接 \(\texttt{std::set}\) 上删除,复杂度是 \(\mathcal{O}(n \log^2 n)\)(也可以用可删堆维护得到常数小一些的 \(\log^2\))。
但这里枚举起点和二分的复杂度又是怎么保证的呢?这其实就是一个把大区间 \([pre, nxt)\) 分成 \([pre, h)\) 和 \([h, nxt)\) 的过程,即启发式分裂,是 \(\mathcal{O}(n \log n)\) 的,再加上一个二分就是 \(\mathcal{O}(n \log^2 n)\)。
每次查询时只要到树状数组里查,复杂度 \(\mathcal{O}(m \log n)\)。
故总时间复杂度 \(\mathcal{O}(n \log^2 n + m \log n)\)。
代码:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
const int MaxN = 100000;
int N, M;
std::set<int> A;
std::set<int> S[MaxN + 5];
std::vector<int> Fac[MaxN + 5];
struct FenwickTree {
long long t[MaxN + 5];
inline int lowbit(int i) { return i & -i; }
inline void update(int x, long long v) { for (int i = x; i <= N; i += lowbit(i)) t[i] += v; }
inline long long query(int x) { long long res = 0; for (int i = x; i > 0; i -= lowbit(i)) res += t[i]; return res; }
inline long long qrange(int l, int r) { return query(r) - query(l - 1); }
};
FenwickTree T;
void solve() {
for (int i = 1; i <= N; ++i)
for (int j = 1; (j - 1) * i <= N; ++j)
S[i].insert(j);
for (int i = 1; i <= N; ++i) {
for (int j = 0; j * i <= N; ++j)
Fac[i * j].push_back(i);
S[i].insert(*(--S[i].end()) + 1);
}
for (int i = 1; i <= N; ++i)
T.update(i, *S[i].begin());
A.insert(0), A.insert(2 * N);
for (int q = 1; q <= M; ++q) {
int opt;
scanf("%d", &opt);
if (opt == 1) {
int x;
scanf("%d", &x);
if (A.count(x) != 0) continue;
std::set<int>::iterator iter_nxt = A.lower_bound(x), iter_pre = iter_nxt;
iter_pre--;
int pre = *iter_pre, nxt = *iter_nxt;
A.insert(x);
if (x - pre < nxt - x) {
for (int i = pre; i < x; ++i) {
std::vector<int>::iterator it = std::lower_bound(Fac[i].begin(), Fac[i].end(), x - i);
while (it != Fac[i].end()) {
int d = *it;
if (i + d >= nxt) break;
int blocknum = (x - 1) / d + 1;
T.update(d, -*S[d].begin());
S[d].erase(blocknum);
T.update(d, *S[d].begin());
it++;
}
}
} else {
for (int i = x; i < nxt; ++i) {
std::vector<int>::iterator it = std::upper_bound(Fac[i].begin(), Fac[i].end(), i - x);
while (it != Fac[i].end()) {
int d = *it;
if (i - d < pre) break;
int blocknum = (x - 1) / d + 1;
T.update(d, -*S[d].begin());
S[d].erase(blocknum);
T.update(d, *S[d].begin());
it++;
}
}
}
} else {
int l, r;
scanf("%d %d", &l, &r);
printf("%lld\n", T.qrange(l, r));
}
}
}
int main() {
scanf("%d %d", &N, &M);
solve();
return 0;
}
标签:algo bre lowbit 两种 back 怎么 ret while tree
原文地址:https://www.cnblogs.com/tweetuzki/p/12944043.html