CF348C 题解

很好的一道根号题。

题意

给定一个序列 \(a_1, a_2, \cdots, a_n\) 以及 \(m\) 个集合 \(S_1, S_2, \cdots, S_m\)。每个集合中的元素都代表序列 \(a\) 中的一个下标。

现在有 \(q\) 个询问,每个询问有两种类型:

  • + k x,表示将所有 \(a_{S_{k, i}}\) 加上 \(x\)

  • ? k,表示查询 \(\sum\limits_{i} a_{S_{k, i}}\)

\(n, m, q, \sum |S_k| \le 10^5\)

题解

首先这个询问类型挺像操作分块,那么往这方面想一想。

当从一个块跳到下一个块,需要重建 \(a\) 序列,这个并不难做,只需要记一个 \(tag_i\) 表示 \(S_i\) 被加上了多少就行了。考虑块内的操作对询问的影响,操作 + i x 对询问 ? j 的影响是让 \(j\) 的答案加上了 \(x \cdot \left(|S_i \cup S_j|\right)\),那么问题转化成了怎么 \(O(1)\) 回答 \(\left(|S_i \cup S_j|\right)\) 的大小。

这个东西肯定是需要预处理的,考虑根号分治,即将集合分为两类(假设 \(n, m, q, \sum |S_k|\) 同阶):\(|S_k| > \sqrt{n}\)大集合\(|S_k| \le \sqrt{n}\) 的是小集合,预处理每个大集合与其他集合的交集大小,信息量是 \(O(n\sqrt{n})\) 的,可以存下。接着在询问的时候,考虑块内的操作 + i x 对当前询问 ? j 的影响,分为 \(2\) 类情况:

  • \(S_i\) 或者 \(S_j\) 中的一个是大集合,可以直接回答交集大小。

  • \(S_i\)\(S_j\) 都是小集合,因为没存它们的信息,不能直接回答,特殊处理一下。因为有 \(S_i, S_j \le \sqrt{n}\) 的条件,所以可以新开一个数组 \(b_1, b_2, \cdots, b_n\) 表示当前块内小集合的修改,修改 \(S_i\) 的时候将 \(b_{S_{i, k}}\) 加上 \(x\),在查询 \(S_j\) 的时候查询 \(\sum b_{S_{j, k}}\) 就能得到小集合之间的贡献了。

总计时间复杂度 \(O(n \sqrt{n})\)

(好像有不用操作分块的做法,时间复杂度都也是 \(O(n \sqrt{n})\),可能更好写,可以学习一下)

code:

#include <bits/stdc++.h>
#define vi vector <int>
#define vl vector <i64>
#define pii pair <int, int>
#define pll pair <i64, i64>
#define mp make_pair
#define tpi tuple <int, int, int>
#define tpl tuple <i64, i64, i64>
#define mt make_tuple
#define eb emplace_back
#define pb push_back
#define ft first
#define sd second
#define bg begin()
#define ed end()
#define mx_ele max_element
#define mn_ele min_element
#define sz(x) (int)(x.size())
#define alls(x) x.bg, x.ed
#define rep(i, l, r) for (int i = (l); i <= (int)(r); ++i)
#define re(i, l, r) rep(i, (l), (r) - 1)
#define per(i, r, l) for (int i = (r); i >= (int)(l); --i)
#define er(i, r, l) per(i, (r) - 1, (l))
#define repp(i, v) for (auto i : v)
#define i64 long long
#define ld long double
using namespace std;
const int inf = 1E9;
const i64 INF = 1E15;
// #define int i64
const int N = 1E5 + 5;
int n, m, q, sL, L, pos[N];
i64 a[N], b[N], tag[N], sum[N];
vi S[N], d[N], id;
vector <pair <int, int>> v;
void rebuild() {
  rep(i, 1, m) tag[i] = 0;
  for (auto [k, x] : v) tag[k] += x;
  v.clear();
  rep(i, 1, n) b[i] = 0;
  rep(i, 1, m) {
    for (auto j : S[i]) a[j] += tag[i];
  }
  re(i, 0, sz(id)) {
    int j = id[i]; sum[i] = 0;
    for (auto p : S[j]) sum[i] += a[p];
  }
}
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(nullptr); cout.tie(nullptr);
  cin >> n >> m >> q;
  rep(i, 1, n) cin >> a[i];
  rep(i, 1, m) {
    int k; cin >> k; L += k;
    while (k--) {int x; cin >> x; S[i].eb(x);}
    sort(alls(S[i]));
  } sL = sqrt(L);
  rep(i, 1, m) if (sz(S[i]) >= sL) id.eb(i);
  vi vis(n + 1);
  rep(i, 1, m) d[i].resize(sz(id));
  re(i, 0, sz(id)) {
    fill(alls(vis), 0);
    int j = id[i];
    for (auto p : S[j]) vis[p] = 1;
    rep(k, 1, m)
      d[k][i] = count_if(alls(S[k]), [&](auto x) {return vis[x];});
    pos[j] = i;
  }
  rebuild();
  int bl = sqrt(q);
  rep(i, 1, q) {
    char opt; int k; cin >> opt >> k;
    if (opt == '+') {
      int x; cin >> x; v.eb(k, x);
      if (sz(S[k]) < sL) {
        for (auto p : S[k])
          b[p] += x;
      }
    } else {
      i64 ans = 0; 
      if (sz(S[k]) >= sL) {
        int pid = pos[k]; ans = sum[pid];
        for (auto [i, x] : v) {
          ans += 1LL * x * d[i][pid];
        }
      } else {
        for (auto p : S[k])
          ans += b[p] + a[p];
        for (auto [i, x] : v) {
          if (sz(S[i]) < sL) continue;
          ans += 1LL * x * d[k][pos[i]];
        }
      }
      cout << ans << '\n';
    }
    if ((sz(v) + 1) == bl) rebuild();
  }
}
posted @ 2024-04-10 00:36  CTHOOH  阅读(32)  评论(0)    收藏  举报