Solution Set #4

P15582 [KTSC 2026] 平衡序列(倍增,找性质)

容易发现好区间的长度一定形如 \(2^k-1\),那么可设 \(f_{i,j}\) 表示 \([j-2^i+1,j+2^i-1]\) 是否合法,单次询问可做到 \(O(n\log n)\)

修改看起来很难做,但是注意到对于每个区间长度 \(2^k-1\),包含某个固定点 \(x\) 的区间至多有两个,因为如果有大于等于三个,则必须会有一个区间的中心在另一个区间里面,由于每个区间的中心都必须是区间最大值,这就出现矛盾了。所以单次修改 \(f_{i,j}\) 的变化量只有 \(O(\log n)\),维护一下每层变化的位置即可。时间复杂度是 \(O((n+q)\log n)\)

对于这类动态修改维护答案的题,如果不知道怎么修改,可以想想答案的变化量会不会不大。

代码
#include <bits/stdc++.h>

#ifdef ORZXKR
#include "grader.cpp"
#endif

const int kMaxN = 1e5 + 5;

int n, ans;
int a[kMaxN];
bool f[18][kMaxN];

bool check(int k, int x) {
  if (x <= (1 << (k - 1)) || x > n - (1 << (k - 1))) return 0;
  int i = x - (1 << (k - 1)), j = x + (1 << (k - 1));
  return a[x] > a[i] && a[x] > a[j] && f[k - 1][i] && f[k - 1][j];
}

long long initialize(int N, std::vector<int> A) {
  n = N;
  for (int i = 1; i <= n; ++i) a[i] = A[i - 1];
  for (int i = 1; i <= n; ++i) f[0][i] = 1, ++ans;
  for (int i = 1; i <= std::__lg(n); ++i) {
    for (int j = 1; j <= n; ++j) {
      f[i][j] = check(i, j);
      ans += f[i][j];
    }
  }
  return ans;
}

long long update_sequence(int p, int v) {
  a[++p] = v;
  std::vector<int> vec = {p};
  for (int k = 1; k <= std::__lg(n); ++k) {
    std::vector<int> vv = {p};
    for (auto x : vec) {
      if (x > (1 << (k - 1))) vv.emplace_back(x - (1 << (k - 1)));
      if (x <= n - (1 << (k - 1))) vv.emplace_back(x + (1 << (k - 1)));
    }
    std::sort(vv.begin(), vv.end());
    vv.erase(std::unique(vv.begin(), vv.end()), vv.end());
    vec.clear();
    for (auto x : vv) {
      if (check(k, x) != f[k][x]) {
        f[k][x] ^= 1;
        if (f[k][x]) ++ans;
        else --ans;
        vec.emplace_back(x);
      } else if (x == p) {
        vec.emplace_back(x);
      }
    }
  }
  return ans;
}
posted @ 2026-03-04 14:57  下蛋爷  阅读(1)  评论(0)    收藏  举报