假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。
现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。
接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 n 和 m。
接下来 n 行,每行包含两个整数 x 和 c。
再接下来 m 行,每行包含两个整数 l 和 r。
输出格式
共 m 行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤109,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
观察数据范围
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef pair<int,int>PII;
map<int,int>mp;
int n,m,x,c,l,r;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>x>>c;
if(mp.find(x)==mp.end())mp[x]=c;
else mp[x]+=c;
}
int sum=0;
vector<PII>v;
for(auto a:mp){
v.push_back({a.first,sum});
sum+=a.second;
}
v.push_back({1e9+1,sum});
while(m--){
cin>>l>>r;
auto p1=upper_bound(v.begin(),v.end(),PII{l,-1e9});
auto p2=upper_bound(v.begin(),v.end(),PII{r,1e9+1});
cout<<p2->second-p1->second<<endl;
}
}
这里提供一种map + upper_bound()的解法
时间复杂度 O((n + m) * log n)
将各个出现过的x映射到map中
计算一下前缀和
upper_bound()查找l与r
C++ 代码
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define pii pair<int, int>
const int inf = 1e9 + 5;
vector<pii> a;
map<int, int> mp;
int main()
{
int n, m;
cin >> n >> m;
while(n --)
{
int x, c;
cin >> x >> c;
if(mp.find(x) == mp.end()) mp[x] = c; //x未出现过
else mp[x] += c; //x出现过
}
int sum = 0;
for(pii x : mp) //计算前缀和
{
a.push_back({x.first, sum}); //这里的sum不包含x.first上的值,方便使用upper_bound()
sum += x.second;
}
a.push_back({inf, sum}); //最后加一个无穷大的点,方便处理
//因为mp是有序的,所以a是有序的
while(m --)
{
int l, r;
cin >> l >> r;
auto p1 = upper_bound(a.begin(), a.end(), (pii){l , -inf}); //找到第一个大于等于l的点
auto p2 = upper_bound(a.begin(), a.end(), (pii){r , inf}); //找到第一个大于r的点
cout << p2 -> second - p1 -> second << endl;
}
return 0;
}
作者:空空如也
链接:https://www.acwing.com/solution/content/12167/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
可以用TreeMap这个集合,完成去重、排序、二分查找,所以代码要少一些。
解释一下TreeMap:底层是红黑树实现的。可以简单理解为有序的HashMap。
getOrDefault(key, default): 获取key对应的value,如果没有key,则返回默认值default。
ceilingEntry(key): 返回大于等于key的Entry(二分)
lowerEntry(key): 返回小于key的Entry(二分),注意,如果没有key,会报空指针异常。
java 代码
import java.util.Scanner;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt(), m = in.nextInt();
int[][] q = new int[m][2];
TreeMap<Integer, Integer> map = new TreeMap<>();
for (int i = 0; i < n; i++) {
int add1 = in.nextInt();
int add2 = in.nextInt();
map.put(add1, map.getOrDefault(add1, 0) + add2);
}
for (int i = 0; i < m; i++) {
q[i][0] = in.nextInt();
map.put(q[i][0], map.getOrDefault(q[i][0], 0));
q[i][1] = in.nextInt();
map.put(q[i][1], map.getOrDefault(q[i][1], 0));
}
Object[] keys = map.keySet().toArray();
for (int i = 1; i < keys.length; i++) { // 前缀和
map.put((Integer) keys[i], map.get(keys[i-1]) + map.get(keys[i]));
}
for (int i = 0; i < m; i++) {
if(q[i][0] == (Integer)keys[0]){ // 避免空指针异常
System.out.println(map.ceilingEntry(q[i][1]).getValue());
}else{
System.out.println(map.ceilingEntry(q[i][1]).getValue() - map.lowerEntry(q[i][0]).getValue());
}
}
}
}
作者:quinlandai@163.com
链接:https://www.acwing.com/solution/content/50620/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
#include<bits/stdc++.h>
using namespace std;
vector<int>xs;
typedef pair<int, int> PII;
vector<PII>op;
vector<PII>q;
int n,m,x,c,l,r;
int sum[100010];
int a[100010];
int get(int x){
return lower_bound(xs.begin(),xs.end(),x)-xs.begin();
}
int find(int x){//找x对应的下标也就是第一个大于等于x的数字
int l=0,r=xs.size()-1;
while(l<r){
int mid=l+r>>1;
if(xs[mid]>=x)r=mid;
else l=mid+1;
}
return r+1;
//这里为什么要加1呢
//原因是把1,20,300,4000,5万,60万映射到下标012345不好做前缀和操作
//干脆直接映射到下标123456,这样就用到了前缀和数组sum【0】=0
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>x>>c;
op.push_back({x,c});
xs.push_back(x);
}
for(int i=0;i<m;i++){
cin>>l>>r;
q.push_back({l,r});
xs.push_back(l);
xs.push_back(r);
}
sort(xs.begin(),xs.end());
xs.erase(unique(xs.begin(),xs.end()),xs.end());
for(int i=0;i<n;i++){
int x=find(op[i].first);
int c=op[i].second;
a[x]+=c;
}
for(int i=1;i<=xs.size();i++){
sum[i]=sum[i-1]+a[i];
}
for(int i=0;i<m;i++){
int l=q[i].first;
int r=q[i].second;
l=find(l);
r=find(r);
//cout<<"l="<<l<<" r="<<r<<endl;
cout<<sum[r]-sum[l-1]<<endl;
}
}
部分内容转载自https://blog.csdn.net/jsjwangxinleijsj/article/details/82020825
upper_bound():返回的是被查序列中第一个大于查找值得指针;
lower_bound():返回的是被查序列中第一个大于等于查找值的指针;
如下图所示:
一、lower_bound;
用法:int t=lower_bound(a+l,a+r,m)-a//由于返回的值为指针所以要用返回值减去a才能得到目标值的下标
解释:在升序排列的a数组内二分查找[l,r)区间内的值为m的元素。返回m在数组中的下标。
特殊情况:
1.如果m在区间中没有出现过,那么返回第一个比m大的数的下标。
2.如果m比所有区间内的数都大,那么返回r。这个时候会越界,小心。
3.如果区间内有多个相同的m,返回第一个m的下标。
二、upper_bound
用法:int t=upper_bound(a+l,a+r,m)-a
解释:在升序排列的a数组内二分查找[l,r)区间内的值为m的元素。返回m在数组中的下标+1。
特殊情况:
1.如果m在区间中没有出现过,那么返回第一个比m大的数的下标。
2.如果m比所有区间内的数都大,那么返回r。这个时候会越界,小心。
3.如果区间内有多个相同的m,返回最后一个m的下标+1。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n=10;
int a[10]={1,1,1,3,3,5,5,5,5,6};
int main()
{
for(int i=0;i<n;i++)cout<<a[i]<<" ";
cout<<endl;
while(scanf("%d",&k))
{
cout<<k<<"的第一个大于等于它的位置在"<<((lower_bound(a,a+n,k))-a)+1<<endl;
}
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n=10;
int a[10]={1,1,1,3,3,5,5,5,5,6};
int main()
{
for(int i=0;i<n;i++)cout<<a[i]<<" ";
cout<<endl;
while(scanf("%d",&k))
{
cout<<k<<"的第一个大于它的位置在"<<((upper_bound(a,a+n,k))-a)+1<<endl;
}
}