P7603 [THUPC 2021] 鬼街 题解

Description

那条街有“鬼街”之称,十年前是 A 市最繁华的地段之一,然而如今这里已无活人居住。

街边七零八落地排着 \(n\) 座房子,每栋房子都有一个 \(1\)\(n\) 之间的独一无二的编号,用仿佛来自地狱的黑漆涂在破瓦残砖上,在黄尘中隐隐若现。

传说,这条街上的鬼是与别处的鬼是不同的,它们喜欢研究数论,会根据数字的性质来选择自己的生活,所以它们才为每一栋房子都画上了编号。

新上任的 A 市市长并不相信魑魅魍魉的传言,为了探清真相,他决定为这条街装上灵异事件监控器。

下面有 \(m\) 个事件依次发生。

  • 灵异事件:在以 \(x\) 的所有质因子为编号的房子里,都发生了 \(y\) 次闹鬼。由于神秘的原因,次数 \(y\) 可能为 \(0\)
  • 监控事件:有一个监控器被安装,其监控以 \(x\) 的所有质因子为编号的房子,当累计的闹鬼总次数达到阈值 \(y\) 时,该监控器会触发报警(若 \(y = 0\),则不论被监控的房子是哪几栋,下一次灵异事件都会立即触发该监控器的报警)。不同房子发生的灵异事件次数会被分开统计,不同的监控器互不影响。所有的监控器被从 \(1\) 开始依次编号。

请将所有的报警反馈给市长,即每个灵异事件之后,有哪些监控器被触发。

\(1<n,m\leq 10^5\),强制在线。

Solution

首先这类警报器题有个传统的通用做法是把一个监控挂在其所监视的 \(\omega\) 个房子上,每个房子设置一个阈值 \(\left\lceil\frac{v}{\omega}\right\rceil\)

修改时暴力修改每个房子上的监控的阈值,如果某个监控在当前房子到达阈值就重构这个监控的所有阈值。每次重构时监控的 \(v\) 都会不超过原来的 \(\frac{\omega-1}{\omega}\),所以均摊复杂度是 \(O\left(\omega\log_{\frac{\omega}{\omega-1}}v\log q\right)\),总复杂度即为 \(O\left(q\omega\log_{\frac{\omega}{\omega-1}}v\log q\right)\)

后来 zky 提出的做法也是在这个上面改进的。

同样是维护阈值,原来的做法比较慢是因为维护阈值时要用优先队列,这样挂在某个房子上的所有监控是否需要重构是相对独立的,很浪费。

考虑修改阈值的定义,对于一个监控,定义 \(h\) 为最大的数,满足 \(\omega\cdot 2^h\leq v\),我们把这个 \(h\) 挂到房子上,当一个房子由 \(a_x\) 变为 \(a_x+w\) 时,只要经过 \(2^h\) 的倍数就重构,每个监控的重构次数仍然是 \(O(\omega\log v)\)

因为 \(a_x\) 修改后经过两次 \(2^h\) 的倍数后会比原来增加至少 \(2^h\),要让 \(h\) 减少 \(1\),需要至多 \(2\cdot \frac{\omega\cdot 2^{h+1}-\omega\cdot 2^h}{2^h}=2\omega\) 次这样的减少操作,所以重构次数就是对的。

现在维护房子就不用优先队列了,因为我们只关心 \(h\) 的大小,而 \(h\) 只有 \(O(\log v)\) 级别,直接 vector 维护即可。

时间复杂度:\(O(q\omega\log v)\)

这个做法常数也比较小,因为这里的 \(\log\)\(\log_2\),只有 \(2\) 的常数,比原先的 \(\log_{\frac{6}{5}}\) 快了不少。

Code

#include <bits/stdc++.h>

// #define int int64_t

using i64 = int64_t;

const int kMaxN = 1e5 + 5;

int n, m, cnt;
int x[kMaxN];
i64 y[kMaxN], a[kMaxN], fir[kMaxN], lstv[kMaxN];
std::vector<int> zero, prime[kMaxN], vec[kMaxN][33];

void prework() {
  for (int i = 2; i <= n; ++i) {
    if (!prime[i].size()) {
      for (int j = i; j <= n; j += i)
        prime[j].emplace_back(i);
    }
  }
}

int getv(i64 x, i64 y) {
  // assert(x >= 0);
  // x = std::max<i64>(x, 0);
  for (int i = 31; ~i; --i)
    if ((1ll << i) * y < x)
      return i;
  return 0;
}

int geth(i64 l, i64 r) {
  for (int i = 31; ~i; --i)
    if ((r >> i) != (l >> i))
      return i;
  return -1;
}

bool fix(int i) {
  // std::cerr << ++cntt[i] << '\n';
  i64 now = -fir[i];
  for (auto p : prime[x[i]]) now += a[p];
  int nowv = getv(y[i] - now, prime[x[i]].size());
  if (now >= y[i]) return lstv[i] = -1, 1;
  if (nowv == lstv[i]) return 0;
  // std::cerr << "shabi " << lstv[i] << ' ';
  assert(nowv < lstv[i]);
  lstv[i] = nowv;
  // std::cerr << lstv[i] << '\n';
  // std::cerr << y[i] - now << '\n';
  for (auto p : prime[x[i]]) vec[p][lstv[i]].emplace_back(i);
  return 0;
}

std::vector<int> update1(int x, i64 y) {
  int h = geth(a[x], a[x] + y); a[x] += y;
  static bool vis[kMaxN] = {0};
  std::vector<int> id, res;
  for (int b = 0; b <= h; ++b) {
    for (auto i : vec[x][b]) {
      if (lstv[i] == b && !vis[i]) id.emplace_back(i), vis[i] = 1;
    }
  }
  // std::cerr << "??? " << x << ' ' << y << " : " << h << ' ' << id.size() << ' ' << lstv[1] << '\n';
  for (auto i : id) {
    if (fix(i)) res.emplace_back(i);
    vis[i] = 0;
  }
  for (int b = 0; b <= h; ++b) {
    std::vector<int> tmp;
    for (auto i : vec[x][b])
      if (lstv[i] == b)
        tmp.emplace_back(i);
    vec[x][b] = tmp;
  }
  return res;
}

void update2(int x, i64 y) {
  ::x[++cnt] = x, ::y[cnt] = y;
  if (!y) return void(zero.emplace_back(cnt));
  for (auto p : prime[x]) fir[cnt] += a[p];
  int v = getv(y, prime[x].size());
  lstv[cnt] = v;
  for (auto p : prime[x]) vec[p][v].emplace_back(cnt);
}

void dickdreamer() {
  std::cin >> n >> m;
  prework();
  for (int i = 1, lastans = 0; i <= m; ++i) {
    int op, x; i64 y;
    std::cin >> op >> x >> y; y ^= lastans;
    // std::cerr << op << ' ' << x << ' ' << y << '\n';
    if (op == 0) {
      std::vector<int> res;
      for (auto p : prime[x]) {
        std::vector<int> tmp = update1(p, y);
        for (auto id : tmp) res.emplace_back(id);
      }
      for (auto id : zero) res.emplace_back(id);
      zero.clear();
      std::sort(res.begin(), res.end());
      std::cout << (lastans = res.size()) << ' ';
      for (auto id : res) std::cout << id << ' ';
      std::cout << '\n';
    } else {
      update2(x, y);
    }
    // if (i >= 99800) std::cerr << i << ' ' << n << ' ' << m << '\n';
  }
  // for (int i = 1; i <= cnt; ++i) std::cout << cntt[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}
``
posted @ 2025-08-18 14:55  下蛋爷  阅读(14)  评论(0)    收藏  举报