鱼C论坛

 找回密码
 立即注册
查看: 1277|回复: 2

[心之碎片][0701] - F 集合操作

[复制链接]
发表于 2024-7-1 17:37:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
问题描述

给出有序的初始为空的集合, 维护操作
add x 把 x 插入
del x 把 x 删除
sum 求所有 下标 % 5 == 2 的数字的和
操作次数 n < 1e5, x < 1e9


错误原因
思路错误
没有想到权值线段树的权值有序性, 没有思考出左边区间的变化对右边区间下标的影响
反解下标出错了


解题思路
离线, 建立权值线段树, 每个节点存 % 5 的答案和区间长
因为每个数字是唯一的, add 就是 +, del 就是 -, 答案就是根节点的 s[3]


总结
在线题目先想离线怎么做, 一般会好些
看到想一直有序的序列, 或者数字重复次数不多, 值域大时想到权值线段树


  1. #include <bits/stdc++.h>
  2. using namespace std;

  3. typedef long long ll;
  4. const int N = 1e5 + 5;

  5. // 离线操作
  6. // 1add 2del 3sum
  7. struct Quest{
  8.     int type, x;
  9. } Q[N];

  10. // 离散化
  11. int toid[N];
  12. int cnt;
  13. int id(int x){
  14.     return (lower_bound(toid + 1, toid + cnt + 1, x) - toid);
  15. }

  16. int n;

  17. #define lc u<<1
  18. #define rc u<<1|1

  19. // 区间里数字个数, 每个下标 %5 的和
  20. struct Node{
  21.     ll siz, s[5];
  22. } t[N << 2];

  23. void Up(Node & rt, Node & a, Node & b){
  24.     for(int i = 0; i < 5; i++){
  25.         rt.s[i] = a.s[i] + b.s[((i - a.siz) % 5 + 5) % 5];
  26.     }
  27.     rt.siz = a.siz + b.siz;
  28. }

  29. // +/- 一个数字在 p 位置
  30. void Modify(int u, int L, int R, int p, int v){
  31.     if(L == p && R == p){
  32.         t[u].s[1] += (ll)v*(ll)toid[L];
  33.         t[u].siz += v;
  34.         return ;
  35.     }

  36.     int mid = (L + R) >> 1;
  37.     if(p <= mid) Modify(lc, L, mid, p, v);
  38.     if(p > mid) Modify(rc, mid + 1, R, p, v);
  39.     Up(t[u], t[lc], t[rc]);
  40. }

  41. int main(){
  42.     ios::sync_with_stdio(0);
  43.     cin.tie(0);

  44.     cin >> n;
  45.     for(int i = 1; i <= n; i++){
  46.         string s;
  47.         ll x;
  48.         cin >> s;
  49.         if(s[0] != 's'){
  50.             cin >> x;
  51.             toid[++cnt] = x;
  52.             if(s[0] == 'a') Q[i].type = 1;
  53.             else Q[i].type = 2;
  54.             Q[i].x = x;
  55.             continue;
  56.         }
  57.         Q[i].type = 3;
  58.     }

  59.     sort(toid + 1, toid + cnt + 1);
  60.     int c = unique(toid + 1, toid + cnt + 1) - toid - 1;
  61.     cnt = c;

  62.     for(int i = 1; i <= n; i++){
  63.         if(Q[i].type == 1){
  64.             Modify(1, 1, cnt, id(Q[i].x), 1);
  65.         }
  66.         else if(Q[i].type == 2){
  67.             Modify(1, 1, cnt, id(Q[i].x), -1);
  68.         }
  69.         else{
  70.             cout << t[1].s[3] << '\n';
  71.         }
  72.     }

  73.     return 0;
  74. }
复制代码



评分

参与人数 1荣誉 +2 鱼币 +2 收起 理由
某一个“天” + 2 + 2 鱼C有你更精彩^_^

查看全部评分

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-7-1 17:38:07 | 显示全部楼层
好吧,这道题目确实有点复杂,不过没关系,鱼油,让我们一起来看看这道涉及集合操作的问题,看看怎么解决吧!

### 问题梳理:

1. **集合操作**:
    - `add x`:将 `x` 插入集合。
    - `del x`:从集合中删除 `x`。
    - `sum`:求所有下标 `% 5 == 2` 的数字之和。

2. **限制条件**:
    - 操作次数 `n < 1e5`(10 万次操作)。
    - 数字 `x < 1e9`(十亿以内的数字)。

### 解题思路:

我们需要一个数据结构来高效地维护这些操作。由于我们需要频繁插入、删除元素以及计算特定下标的数字之和,所以可以考虑以下几种方法:

1. **基于平衡树的数据结构**(如红黑树、AVL树等):
    - 能够高效地进行插入、删除操作。
    - 维持数据的有序性,方便我们计算特定下标的数据之和。

2. **离线处理与离散化**:
    - 将大范围的数字通过映射压缩到小范围内,这样能有效减少空间消耗,并加快查询速度。

**具体实现步骤**:

1. **离线处理**:将所有操作记录下来,避免实时处理带来的复杂度问题。
2. **离散化**:将所有出现过的数字离散化为小范围的编号。
3. **用平衡树来维护集合**:这样我们可以高效地进行插入、删除操作,并且能够快速找到特定下标的数据。

### 代码实现:

话不多说,上代码!不过还是先给鱼油们贴几行注释:

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <set>

  5. using namespace std;

  6. struct Quest {
  7.     int type; // 1 for add, 2 for del, 3 for sum
  8.     int x;
  9. };

  10. vector<int> toid; // 用于离散化
  11. vector<Quest> Q; // 存储所有操作

  12. int id(int x) {
  13.     return lower_bound(toid.begin(), toid.end(), x) - toid.begin();
  14. }

  15. int main() {
  16.     int n;
  17.     cin >> n;

  18.     Q.resize(n);

  19.     for (int i = 0; i < n; ++i) {
  20.         string op;
  21.         int x;
  22.         cin >> op;
  23.         if (op == "sum") {
  24.             Q[i] = {3, 0};
  25.         } else {
  26.             cin >> x;
  27.             toid.push_back(x);
  28.             if (op == "add") {
  29.                 Q[i] = {1, x};
  30.             } else if (op == "del") {
  31.                 Q[i] = {2, x};
  32.             }
  33.         }
  34.     }

  35.     sort(toid.begin(), toid.end());
  36.     toid.erase(unique(toid.begin(), toid.end()), toid.end());

  37.     multiset<int> ms;

  38.     for (const auto& q : Q) {
  39.         if (q.type == 1) {
  40.             ms.insert(id(q.x));
  41.         } else if (q.type == 2) {
  42.             ms.erase(ms.find(id(q.x)));
  43.         } else if (q.type == 3) {
  44.             int sum = 0;
  45.             int idx = 0;
  46.             for (const auto& num : ms) {
  47.                 if (idx % 5 == 2) {
  48.                     sum += toid[num];
  49.                 }
  50.                 ++idx;
  51.             }
  52.             cout << sum << endl;
  53.         }
  54.     }
  55.     return 0;
  56. }
复制代码


### 代码解析:

1. **离线处理**:我们先把所有的操作读进来存入向量 `Q` 中。
2. **离散化**:将出现的所有数字存入 `toid` 向量并进行排序和去重,使得每个数字都映射到一个唯一的小范围编号。
3. **高效查询**:利用 `multiset` 来维护集合,`multiset` 能够高效处理插入和删除操作。
4. **下标计算**:遍历 `multiset`,计算所有下标 `% 5 == 2` 的数字总和。

这样,我们就能够高效地完成这道题目啦!希望这个思路和代码对你有帮助,有什么问题随时来问哈!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-1 19:49:29 | 显示全部楼层
这麽好的东西必须支持
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-27 16:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表