3 条题解
-
1
S001B. 催化剂
给定一个长度为 的序列 , 位置表示有一个种类为 的糖果( 可以重复,即同一个种类有多个糖果),接下来有 次查询:
1 x
:表示种类 多了 个糖果2 x
:表示种类 少了 个糖果3 k
:输出将所有糖果分给 个小朋友的最小愤怒值之和- 愤怒值:令第 个小朋友所拥有的第 种糖果的数量为 ,则愤怒值之和为
若第 种糖果的数量为 ,则对于 个小朋友,最小愤怒值为 ,其实就是每种糖果平均分给 个小朋友,可以证明这是最优的。比如说:糖果为 , 个小朋友,划分方案如下。
第一个小朋友 第二个小朋友 那么,问题转化为了动态求出现所有出现次数 的数的和 出现次数 的数的个数。这个是可以通过平衡树来维护的,不过难写且麻烦。
不妨考虑将询问离线,对于一次更改,相当于对所有 询问作统一的更改( 表示当前更改后该糖果种类的数量)。故,考虑将询问排序,每次更改通过树状数组实现即可。
signed main() { cin.tie(0); cout.tie(0); ios::sync_with_stdio(0); cin >> n >> q; int op, x; for (int i = 1; i <= n; i ++) cin >> x, opr[ ++ m] = {x, 1}; while (q -- ) { cin >> op >> x; if (op == 1) opr[ ++ m] = {x, 1}; else if (op == 2) opr[ ++ m] = {x, -1}; else k ++, qry[k] = {x, k}, upd[m].push_back(k); } sort(qry + 1, qry + 1 + k); for (int i = 1; i <= k; i ++) pos[qry[i].se] = i; for (int i = 1; i <= m; i ++) { sum.add(lst[opr[i].fi], -tot[opr[i].fi]); tot[opr[i].fi] += opr[i].se; cnt.add(lst[opr[i].fi], -1); int l = 1, r = k, ans = -1; while (l <= r) { int mid = l + r >> 1; if (qry[mid].fi < tot[opr[i].fi]) l = mid + 1, ans = mid; else r = mid - 1; } if (ans == -1) lst[opr[i].fi] = 0; else lst[opr[i].fi] = k - ans + 1, sum.add(lst[opr[i].fi], tot[opr[i].fi]), cnt.add(lst[opr[i].fi], 1); for (auto v : upd[i]) res[v] = sum.sum(k - pos[v] + 1) - qry[pos[v]].fi * cnt.sum(k - pos[v] + 1); } for (int i = 1; i <= k; i ++) cout << res[i] << endl; return 0; }
信息
- ID
- 3
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 465
- 已通过
- 95
- 上传者