AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 802. 画个图辅助理解~    原题链接    简单

作者: 作者的头像   liangshang ,  2020-05-22 14:58:00 ,  所有人可见 ,  阅读 25191


945


493

C++ 代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 300010; //n次插入和m次查询相关数据量的上界
int n, m;
int a[N];//存储坐标插入的值
int s[N];//存储数组a的前缀和
vector<int> alls;  //存储(所有与插入和查询有关的)坐标
vector<pair<int, int>> add, query; //存储插入和询问操作的数据

int find(int x) { //返回的是输入的坐标的离散化下标
    int l = 0, r = alls.size() - 1;
    while (l < r) {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        int x, c;
        scanf("%d%d", &x, &c);
        add.push_back({x, c});
        alls.push_back(x);
    }
    for (int i = 1; i <= m; i++) {
        int l , r;
        scanf("%d%d", &l, &r);
        query.push_back({l, r});
        alls.push_back(l);
        alls.push_back(r);
    }
   //排序,去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    //执行前n次插入操作
    for (auto item : add) {
        int x = find(item.first);
        a[x] += item.second;
    }
    //前缀和
    for (int i = 1; i <= alls.size(); i++) s[i] = s[i-1] + a[i];
    //处理后m次询问操作
    for (auto item : query) {
        int l = find(item.first);
        int r = find(item.second);
        printf("%d\n", s[r] - s[l-1]);
    }

    return 0;
}

8021.png

8022.png

159 评论


用户头像
DarkCom   2023-01-08 16:17      27    踩      回复

我觉得第二张图的index可以优化一下
alls[]数组的index还是从0开始,而a[]数组和s[]数组的index从1开始。
不过作者写的已经特别详细啦,感谢分享!

用户头像
210853117   2023-01-14 22:02      3    踩      回复

谢谢你~要不然我还蒙在鼓里(//̀Д/́/)


用户头像
艾伦-耶格尔   2023-02-23 17:34      19    踩      回复

orz, 贴一个不用find 的, 用哈希表记录下标


// 思路 : 因为范围太大, 所以不能开一个大数组
//开一个小数组 : 范围就是 add(index) + question(index) , 两者的范围, 排序去重
//我们在index + c, 就是要在新数组的新下标下标操作

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

const int N = 1000010;
int a[N], s[N]; 
typedef pair<int, int> pii;
int main() {
    int n, m;
    cin >> n >> m;
    vector<pii> add, query;
    vector<int> all;
    for(int i = 0; i < n; i++) {
        int l , r;
        cin >> l >> r;
        add.push_back({l, r}); // 存储单独一个 
        all.push_back(l); 
    }

    for(int i = 0; i < m; i++) {
        int l , r;
        cin >> l >> r;
        all.push_back(l);
        all.push_back(r);
        query.push_back({l, r});
    }

    sort(all.begin(), all.end());
    all.erase(unique(all.begin(), all.end()), all.end()); // 去重

    unordered_map<int, int> get_index;
    //获取新下标
    for(int i = 1; i <= all.size(); i++)  get_index[all[i - 1]] = i; // 注意这里 all[i - 1] , 不是i 

    for(int i = 0; i < add.size(); i ++){
        int old_index = add[i].first;
        int c = add[i].second;
        int new_index = get_index[old_index];

        a[new_index] += c;

    }

    for(int i = 1; i <= all.size(); i++){
        a[i] += a[i - 1];
    }

    for(auto p : query) {
        int l = p.first, r = p.second;
        int l2 = get_index[l], r2 = get_index[r];
        cout << (a[r2] - a[l2 - 1]) << endl;
    }

} 
用户头像
acwing_71508   7个月前         踩      回复

写的不错,我也觉得用unordered_map比find()更直观。
不过输入n组数据时,我觉得用x和c比l和r更好,后者看起来像坐标…
另外,定义的前缀和数组s[N]似乎没用到,你直接用了a[N]覆盖掉充当前缀和数组,感觉用s[N]更清楚些。

用户头像
worthy.   7个月前      2    踩      回复

为什么要去重啊,我没有去重也能过得?

用户头像
Camellia_Wang   6个月前         踩      回复
 //处理插入
    for(auto item:add)
    {
        int x=get_index.at(item.first);//通过一个元素找到哈希表的另一个元素
        a[x]+=item.second;
    }
    //处理前缀和
    for(int i=1;i<=alls.size();i++) s[i]=s[i-1]+a[i];
    //处理询问
    for(auto item:query)
    {
        int l=get_index.at(item.first);
        int r=get_index.at(item.second);
        cout<<s[r]-s[l-1]<<endl;
    }

贴一个用上s[N]数组的代码

用户头像
xiaoshabi   4个月前    回复了 worthy. 的评论      2    踩      回复

废话(doge)
去没去重不影响最后的答案,事实上如果不选择去重,最后的运行时间还会少一些
测了一下,去重的版本跑了610+ms,没去重的反而只跑了580+ms

分析
以答主给出的代码为例
对于每个询问中给定的x和c,查找到的结果都是alls数组中x的左端点xl(例如,序列:1、1、2中元素1的左端点为0)
且每次都只会a[xl]上的元素操作,这意味着若alls中有一些下标:a1、a2、a3…ak,满足alls[a1] = alls[a2] = .... = alls[ak]
最后所有的操作都必定只会影响a[a1],因为a[a1]是左端点元素
因而,对于s中满足:alls[k1] = alls[k2] = .... = alls[kn]的连续下标:k1、k2…kn,只有a[k1]不等于0,其他下标的元素都为0

所以这种情况下,没有去重和去重之后每次询问得到的结果是一样的

再举个栗子

假设排序之后的alls是:1、1、3、3、4

去重之后是:1、3、4

执行操作“将x=1的元素加上2”后a数组是:2、0、0(索引从1开始计数)

前缀和数组应为:0、3、6、10(最开始的数值0代表没有元素被考虑累加的情况)

这时如果给出一个询问——l = 1, r = 3

那么find(l) = 1, find(r) = 2

所以结果为s[2] - s[1 - 1] = s[2] - s[0] = 6

而如果不去重呢?

此时alls是:1、1、3、3、4
执行操作“将x=1的元素加上2”后a是:2、0、0、0、0

前缀和数组应为:0、3、3、6、6、10

这时如果给出一个询问——l = 1, r = 3

那么find(l) = 1, find(r) = 2

所以结果为s[2] - s[1 - 1] = s[2] - s[0] = 6

前后结果保持不变

用户头像
ㅤ渴望变强   2个月前    回复了 xiaoshabi 的评论         踩      回复

find(r)应该等于3吧

用户头像
xiaoshabi   2个月前    回复了 ㅤ渴望变强 的评论         踩      回复

对,当时写错了O.O

用户头像
早上起来胡辣汤就着算法题会不会更下饭点   2个月前    回复了 worthy. 的评论      2    踩      回复

没去重的话,那么重复的alls里面的数值会多次映射到数组a里面。但是我们二分分出来的是最左边的值,也就是说诸多重复的数值,只有最左边的数值的坐标映射到数组a里面,其余重复的数值映射的值都为0(显然会增加数组a的长度,占用空间)。仅仅是增加了数组a的长度,那自然不会影响前缀和的值


用户头像
ACRush   2022-02-18 14:51      16    踩      回复

清晰易懂 我认为比前两个题解更好 tql

用户头像
林杜甫   2022-03-12 20:48      1    踩      回复

+1

用户头像
AcWinwinwin   2022-04-07 20:41      2    踩      回复

确实,这篇画图讲解太清晰了

用户头像
时崎狂三   2022-09-29 20:10         踩      回复

雀氏

用户头像
YzWait2-4YsZth   2022-10-12 20:44         踩      回复

yyds

用户头像
Love_cheng   11个月前         踩      回复

+1


用户头像
小希别adandon   2023-02-10 11:20      15    踩      回复

为什么alls要存储l与r呢?
答:我们最终要对[l, r]求和,自然就不能使用原来的数组求和(数据太大,过于离散)。
所以我们把l和r也映射到alls数组(l,r的值若没有add的操作,即插入不会在l或r的位置,值就为0(全局变量的默认值为0))。这样数轴就会变短很多(最大到3*10^5),我们就可以使用前缀和求和达到O(1)。


用户头像
Pobz   2023-01-06 15:45      6    踩      回复

用户头像
Pobz   2023-01-06 17:21         踩      回复

图解太棒了呀,赞


用户头像
AC-fish   2022-09-07 15:11      4    踩      回复

太棒了这个图。清晰易懂,大佬我可以搬一个到我打卡里面吗!!?


用户头像
isJerryNa   2022-11-14 20:51      3    踩      回复

感谢哥,爱你


用户头像
APOLLONIA   2023-06-17 11:19      2    踩      回复

为什么要将所需要使用的坐标进行排序和去重呢

用户头像
ㅤ渴望变强   2个月前         踩      回复

我是这样理解的 all数组存放的是下标 既然是下标 那每一个下标都是唯一的 所以去重 排序是因为要使用二分查找 所以需要排序 不知道对不对😂

用户头像
ㅤ渴望变强   2个月前         踩      回复

楼上看了一个帖子 可以不用去重

用户头像
Travis_0   1个月前    回复了 ㅤ渴望变强 的评论      1    踩      回复

去重之后二分比较方便。(类似地,把查询操作里的 l 和 r 也一起维护进去,同样是方便写二分)
上述两点分别确保了我们查询的值 唯一 & 存在。这样在写二分时就可以统一按照 <x 和 >=x 划分区间,找右侧分界点,此时该分界点一定就是那个值。假设没有唯一或者存在的前提,二分查找时就要额外考虑一些问题了。

用户头像
Travis_0   1个月前    回复了 ㅤ渴望变强 的评论      1    踩      回复

排序的话,是因为我们需要维护下标的全序关系。以这个题为例,它涉及到区间操作,所以我们要确保下标映射之后的顺序还是一样的。所以这里的因果关系是,进行了排序 -> 可以用二分查找。如果这里用 map 而不是二分的话,也是一样需要排序的。


用户头像
夏目浅石   2023-01-15 23:16      2    踩      回复

讲的太透彻了,我想搬到我自己的博客可以么??追加链接嘿嘿


用户头像
假如有点困   2022-05-09 09:49      2    踩      回复

这道题解写的最好


用户头像
acwing_06689   4个月前      1    踩      回复

真厉害啊大佬,我测,我看了五天,看到你这一篇题解,真的突然明白了,太感谢了啊
,


用户头像
九百月_7   5个月前      1    踩      回复

这个是什么意思啊for (auto item : query) {

用户头像
Hollo   3个月前         踩      回复

从头遍历query里面的所有元素,简单来说item=query[i]


用户头像
Qiutt   5个月前      1    踩      回复

这个为啥要排序去重啊


用户头像
叮铃_1   8个月前      1    踩      回复

大佬牛p!!!


用户头像
JoskerMb   9个月前      1    踩      回复

niubi


用户头像
Leo.X   10个月前      1    踩      回复

写的太棒了,二刷我原本也打算写个题解,一看你的觉得完全没有写的必要了。


用户头像
shiyasai   2023-03-21 11:05      1    踩      回复

请问这个alls代表哪个英文单词啊,百思不得其解。

用户头像
ALLBYYOURSELF   2023-04-25 09:07         踩      回复

all+s,全部的


用户头像
subtractionpainter   2023-02-19 23:49      1    踩      回复

这图太清晰了吧 谢谢


用户头像
acbaby   2022-12-08 21:58      1    踩      回复

恩比,画图确实清晰多了


用户头像
Surss   2022-11-17 11:16      1    踩      回复

我爱你


你确定删除吗?

© 2018-2024 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息