题解:P11955 「ZHQOI R1」覆盖

· · 题解

对于一颗线段树,它的结构如图所示。一定是先有红色,再有绿色,再有蓝色,再有紫色。如果靠前的颜色没有那么靠后的颜色不可能出现。

我们先考虑上一层(黑色)都已经处理完,新的一层会有什么影响,即已知 f_{2^j}f_{2^j+k}

y 为上层的节点个数(图示为 4),t=\frac{y}{2}x 为上层有儿子的节点的个数。

现在能 O(\log n)f_i 了,从上往下每层去做即可。

现在所求的东西是 \sum f_i。发现每层增加的数是一个公差为一的等差数列加 (f_{2^j}+t)\times (f_{2^j}+t-1),随便算算就行了。因为是赛时写的,我的具体实现是考虑每个 f_{2^j} 对后面的影响,直接提前加上。

复杂度 O(\log n)

#include <bits/stdc++.h>

using namespace std;

#define IL inline
using ubt = long long;

template <typename T = int>
IL T _R() {
  T s = 0, w = 1;
  char c = getchar();
  while (!isdigit(c)) w = c == '-' ? -1 : 1, c = getchar();
  while (isdigit(c)) s = s * 10 + c - 48, c = getchar();
  return s * w;
}

const int mod = 353442899;

IL ubt F(ubt p) { p %= mod; return p * (p + 1) / 2 % mod; }

IL ubt calc(ubt K) {
  if (K <= 2) {
    if (K == 0) return 0;
    if (K == 1) return 1;
    if (K == 2) return 1 + 3;
    assert(false);
  }
  ubt now = 2;
  ubt res = 1 + 3 * (K - 1);
  while (now * 2 <= K) {
    auto y = now >> 1;
    auto det = F(y + 1) + y % mod * ((y - 1) % mod) % mod;
    res += det;
    res += (K - now * 2) % mod * ((y + 1) % mod) % mod;
    res %= mod;
    now <<= 1;
  }

  auto x = K - now;
  if (x) {
    auto y = now >> 1;
    if (x <= y) res += F(x);
    else {
      res += F(y);
      res += y % mod * ((x - y) % mod) % mod;
    }
  }
  return res % mod;
}

IL void Main() {
  auto L = _R<ubt>(), R = _R<ubt>();

  auto res = (calc(R) - calc(L - 1) + mod) % mod;
  printf("%lld\n", res);
}

int main() {
  int Tims = _R();
  while (Tims--) Main();
}

好像有个新规定,放下过题记录吧。