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();
}
}

浙公网安备 33010602011771号