题解:P3527 [POI 2011] MET-Meteors

题解:P3527 [POI 2011] MET-Meteors

题目描述

\(n\) 个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为 \(m\) 份(第 \(m\) 份和第 \(1\) 份相邻),第 \(i\) 份上有第 \(o_i\) 个国家的太空站。

这个星球经常会下陨石雨。BIU 已经预测了接下来 \(k\) 场陨石雨的情况。

BIU 的第 \(i\) 个成员国希望能够收集 \(p_i\) 单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

\(1\le n,m,k\le 3\cdot10^5\)\(1\le p_i,a_i\le 10^9\);64MB。

第一行是两个数 \(n,m\)

第二行有 \(m\) 个数,第 \(i\) 个数 \(o_i\) 表示第 \(i\) 段轨道上有第 \(o_i\) 个国家的太空站。

第三行有 \(n\) 个数,第 \(i\) 个数 \(p_i\) 表示第 \(i\) 个国家希望收集的陨石数量。

第四行有一个数 \(k\),表示 BIU 预测了接下来的 \(k\) 场陨石雨。接下来 \(k\) 行,每行有三个数 \(l_i,r_i,a_i\) ,表示第 \(k\) 场陨石雨的发生地点在从 \(l_i\) 顺时针到 \(r_i\) 的区间中(如果 \(l_i \leq r_i\),则是 \(l_i, l_i + 1 \cdots, r_i\),否则就是 \(l_i, l_i + 1, \cdots m - 1, m, 1, 2, \cdots r_i\)),向区间中的每个太空站提供 \(a_i\) 单位的陨石样本。

题解

使用小波矩阵进行二分。关于小波矩阵,请见浅谈 Wavelet Matrix - 洛谷专栏详细揭秘:如何发明小波矩阵 - 洛谷专栏等资料。

先把问题写成数点的形式。每一场流星雨是一个区间加,我们把它差分改成单点加,然后查询就变成前缀和。对于 \(t\) 时刻的在 \(x\) 位置加 \(y\) 操作,把它写成 \((t, x, y)\) 三元组。最后把所有三元组按照 \(t\) 排序,用三元组序列构造小波矩阵,键值为 \(x\),权值为 \(y\)。那么查询 \(t_0\) 时刻结束后一个位置 \(x_0\) 所落下过的陨石数量,就等于做一个二维数点:\(\sum_{(t,x,y)}[t\leq t_0][x\leq x_0]y\)。小波矩阵很擅长做这样的在线静态二维数点,到这里我们直接对每个国家二分答案,就做到 2log 的复杂度。

感觉这个二分的事情和区间 k-th 很像,毕竟,区间 k-th 的一个暴力做法就是二分答案做二维数点。能不能模仿区间 k-th 算法把它优化到 1log 呢?是可以的。从小波矩阵的根出发往下二分,每次计算出左儿子上所有太空站能接收到的陨石数量总和(算前缀和),然后与要求的陨石量相比,决定走到左儿子还是右儿子。时间复杂度 \(O((n+m+k)\log k)\),空间复杂度 \(O(k\log k)\)

本题空间限制比较紧,虽然我们的小波矩阵已经很努力了,但还是有点倒闭。

  1. 破环为链,把询问的集合大小翻一倍,这样差分修改次数就是 \(2q\) 而不是原来最多 \(3q\)
  2. 慎防 vector 重分配导致空间消耗的翻倍,不过好像不用怎么防,原来没写好的就小心一下就行了。
  3. 设置常数 \(c\),把陨石雨平均分成 \(c\) 块,每块分别做,时间复杂度 \(O(((n+m)c+k)\log k)\),空间复杂度 \(O(k\log k/c)\)。取 \(c=8\) 可以通过。

另外有两个难绷的注意点:

  • 注意小波矩阵查询前缀的时候要和查询区间一样写,每次求和都是两个前缀和相减,而不是只有一个前缀和,因为前缀的左端点 \(0\) 也是会变的。
  • 前缀和数组开 long long,然后查询的时候如果当前的陨石数量已经超过需求值了,就及时退出,防止严重溢出。

感觉这个小波矩阵和主席树真的好像啊,有点上位替代的感觉。能离线的话,下次还是写整体二分吧。

再给我多一倍空间,这题就能在线回答了。(给定每个国家的空间站编号后立即回答答案)

倒闭。

代码

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
constexpr int N = 3e5 + 10;
struct bits {
  int n;
  vector<uint64_t> b;
  vector<int> pre;
  bits() = default;
//bits(const vector<int>& vec) : n((int)vec.size() / 64 + 1), b(n), pre(n) {
  void build(const vector<int>& vec) {
    memset(b.data(), 0, sizeof b[0] * b.size());
    for (int i = 0; i < (int)vec.size(); i++) {
      if (vec[i]) b[i >> 6] |= 1ull << (i & 63);
    }
    for (int i = 1; i < (int)pre.size(); i++) {
      pre[i] = pre[i - 1] + __builtin_popcountll(b[i - 1]);
    }
  }
  int ask(int k) const {
    return pre[k >> 6] + __builtin_popcountll(b[k >> 6] & ((1ull << (k & 63)) - 1));
  }
};
struct node {
  int x, y;
};
struct wavelet {
  static constexpr int m = 19;
  int p[m];
  bits b[m];
  vector<uint64_t> s[m];
  void build(vector<node> a) {
    int n = (int)a.size();
    vector<int> tb(n);
    vector<node> tmp(n);
    for (int j = m; j--; ) {
      int &x = p[j] = 0, y = 0;
//    s[j].resize(n + 1);
      memset(s[j].data(), 0, sizeof s[j][0] * s[j].size());
      for (int i = 0; i < n; i++) {
        tb[i] = a[i].x >> j & 1;
        (tb[i] ? tmp[y++] : a[x++]) = a[i];
        s[j][i + 1] = (!tb[i] ? a[i].y : 0) + s[j][i];
      }
//    b[j] = bits(tb);
      b[j].build(tb);
      memcpy(a.data() + x, tmp.data(), sizeof a[0] * y);
    }
  }
  pair<int, int> query(vector<int> S, int req) const {
    int res = 0, q = (int)S.size(), S0 = 0;
    vector<int> tp(q);
    for (int j = m; j--; ) {
      uint64_t sum = 0;
      for (int i = 0; i < q; i++) tp[i] = b[j].ask(S[i]);
      for (int i = 0; i < q; i++) {
        sum = sum + s[j][S[i]] - s[j][S0]; // 前缀求和视作区间
        if (sum >= req) break; // 及时退出
      }
      int tp0 = b[j].ask(S0);
      if (sum >= req) {
        for (int i = 0; i < q; i++) S[i] -= tp[i];
        S0 -= tp0;
      } else {
        req -= sum, res |= 1 << j;
        for (int i = 0; i < q; i++) S[i] = p[j] + tp[i];
        S0 = p[j] + tp0;
      }
    }
    return {res, req};
  }
};
int n, m, req[N], q, pos[N * 2], fans[N];
vector<int> S[N];
vector<node> buc[N * 2];
wavelet wb;
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  cin >> m >> n;
  for (int i = 1, x; i <= n; i++) cin >> x, S[x].push_back(i);
  for (int i = 1; i <= m; i++) cin >> req[i];
  cin >> q;
  int b = max(q / 8, 1);
  for (int i = 0; i < wb.m; i++) {
    wb.s[i].resize((b + 1) * 2);
    wb.b[i].n = (b + 1) * 2 / 64 + 1;
    wb.b[i].b.resize(wb.b[i].n);
    wb.b[i].pre.resize(wb.b[i].n);
  }
  memset(fans, 0x3f, sizeof fans);
  for (int ql = 1; ql <= q; ql += b) {
    int qr = min(ql + b - 1, q);
    for (int i = 1; i <= n * 2; i++) buc[i].clear();
    for (int i = ql; i <= qr; i++) {
      int l, r, w;
      cin >> l >> r >> w;
      if (l > r) r += n; // 断环为链
      buc[l].push_back({i, w});
      buc[r + 1].push_back({i, -w});
    }
    vector<node> vec;
    for (int i = 1; i <= n * 2; i++) vec.insert(vec.end(), buc[i].begin(), buc[i].end()), pos[i] = (int)vec.size();
    wb.build(vec);
//  for (auto [x, y] : vec) debug("%d %d\n", x, y);
    for (int i = 1; i <= m; i++) if (fans[i] >= ql) {
      int sz = (int)S[i].size();
      vector<int> tmp(sz * 2);
      for (int j = 0; j < sz; j++) tmp[j + sz] = pos[S[i][j] + n], tmp[j] = pos[S[i][j]]; // 断环为链
      if (!tmp.empty()) tie(fans[i], req[i]) = wb.query(tmp, req[i]);
      debug("fans[%d] = %d\n", i, fans[i]);
    }
  }
  for (int i = 1; i <= m; i++) {
    auto ans = fans[i];
    if (ans > q) cout << "NIE" << endl;
    else cout << ans << endl;
  }
  return 0;
}
posted @ 2026-01-30 20:02  caijianhong  阅读(11)  评论(0)    收藏  举报