【题解】Code+7 教科书般的亵渎

时间:2020-05-23
本文章向大家介绍【题解】Code+7 教科书般的亵渎,主要包括【题解】Code+7 教科书般的亵渎使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题意:

有一个集合 \(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)\)

但这里枚举起点和二分的复杂度又是怎么保证的呢?这其实就是一个把大区间 \([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;
}

原文地址:https://www.cnblogs.com/tweetuzki/p/12944043.html