编程感悟: 9.28 acwing 802 区间和

题目:

假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。

现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。

接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。

刚开始接触离散化,不是很熟悉,一开始还弄了个二维数组,用来存坐标的值与下标,后来发现很难办,而且空间开得太大了,浪费很多资源

这个时候,我在题解中学到一招,用vector<pair<int,int>>来节省大量空间资源

const int N = 300010;
vector<pair<int, int>>f, s;
int n, m, x, c, l, r;
int a[N];

那么,这样的向量可以存所有的下标与值,还可以用它来存要搜寻的数据,同时向量还有很多数组做不到的骚操作,简单地完成复杂需求

for (int i = 0; i < n; i++) {
        scanf("%d%d", &x, &c);
        f.push_back({ x,c });
    }
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &l, &r);
       /*f.push_back({ l,0 });
        f.push_back({ r,0 });*/(注释的后面解释)
        s.push_back({ l,r });(这里用来存查询)
    }

那么下一个问题:怎么快速查询l与r

一开始,我在想,能不能构造一个函数,通过不断二分,使r的值不断逼近f[x].first(pair里面的第一个数,即下标),但很明显,这样子做很绕,边界问题很难处理

看了解答的思路,我恍然大悟,把l与r也存到f里面,不久让l与r天然是其边界了吗,而且不影响求值,顺着这个思路,很自然就有

f.push_back({ l,0 });

 f.push_back({ r,0 });

后面的显而易见,编一个cmp,升序排列

bool cmp(pair<int, int>a1, pair<int, int>a2) {
    return a1.first < a2.first;
}
sort(f.begin(), f.end(), cmp);

再后面是第二个难点:如何将相同下标的值合并

答案是:再开一个vector

vector<pair<int, int>>temp;
    temp.push_back(f[0]);
    for (int i = 1; i < f.size(); i++) {
        if (f[i].first == temp.back().first) {
            temp.back().second += f[i].second;
        }
        else {
            temp.push_back(f[i]);
        }
    }
    f = temp;

前缀和没有什么好说的,查询次数大就用它

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

最终,来到最后一个难点:怎么找到与目标值相同的f[i].first并取出i,二分的思路是有手就行的,但二分的边界问题一直让人头疼,建议直接背模板,不然像我一样调好几次

int find(int y) {
    int Left = 0, Right = f.size()-1;
    while (Left < Right) {
        int mid = Left + (Right - Left) / 2;
        if (f[mid].first >= y) Right = mid;
        else Left = mid + 1;
    }
    return Right;
}

输出

for (int i = 0; i < m; i++) {
        printf("%d\n", a[find(s[i].second)] - a[find(s[i].first)-1]);
    }

因为这个输出与读入量很大,把我从cin与cout逼到了printf与scanf,scanf在vs2022里面想用还得多写一个取消警告,南蚌

下面是ac的源代码:

#define _CRT_SECURE_NO_WARNINGS   /*让vs能用scanf*/
#include <iostream>
#include<cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 300010;
vector<pair<int, int>>f, s;
int n, m, x, c, l, r;
int a[N];

bool cmp(pair<int, int>a1, pair<int, int>a2) {
    return a1.first < a2.first;
}

int find(int y) {
    int Left = 0, Right = f.size()-1;
    while (Left < Right) {
        int mid = Left + (Right - Left) / 2;
        if (f[mid].first >= y) Right = mid;
        else Left = mid + 1;
    }
    return Right;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        scanf("%d%d", &x, &c);
        f.push_back({ x,c });
    }
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &l, &r);
        f.push_back({ l,0 });
        f.push_back({ r,0 });
        s.push_back({ l,r });
    }
    sort(f.begin(), f.end(), cmp);
    vector<pair<int, int>>temp;
    temp.push_back(f[0]);
    for (int i = 1; i < f.size(); i++) {
        if (f[i].first == temp.back().first) {
            temp.back().second += f[i].second;
        }
        else {
            temp.push_back(f[i]);
        }
    }
    f = temp;
    a[0] = f[0].second;
    for (int i = 1; i < f.size(); i++) {
        a[i] = a[i - 1] + f[i].second;
    }
    for (int i = 0; i < m; i++) {
        printf("%d\n", a[find(s[i].second)] - a[find(s[i].first)-1]);
    }
}
 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值