题目链接(这个比赛已经结束了没法提交了,只能用来看题面,暂时没找到可以提交的网站)
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
int a[100010];
int main(int argc, char const *argv[])
{
int n, m;
long long int sum = 0;//评论区指正
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++)
{
int l = upper_bound(a + i + 1, a + n + 1, abs(m - a[i])) - a;
printf("%d ", l);
int r = lower_bound(a + i + 1, a + n + 1, m + a[i]) - a;
printf("%d\n", r);
sum += r - l;
}
printf("%lld\n", sum);
return 0;
}
1.为什么l用upper
而r用lower
?
给出两个数x,y,我们需要找abs(x-y)<元素个数<x+y,中的元素个数,所以我们需要先找到第一个比abs(x-y)大的(严格大,没有=号)数的下标,同时这个下标是可以取到的(因为这个下标代表的这个数本身就比abs(x-y)大),这样左边的值我们就找到了,下面需要找右面的值。如果有一串序列是a[]={0,3,3,3,3,4},如果我们在求右边边界值的时候也用upper同时我们x+y在这里为3的话,那我们找的就是a[5],也就是4那个值所在的下标(因为upper是严格大于),但是很明显之前还有很多3,这些3同样是不能取的,因为我们要找的是两边之和大于第三边(严格大于而不是大于等于),所以如果我们用upper的话,我们找到的是严格大于x+y的,我们不确定在严格大于x+y之前有没有和x+y相等的数,这样一来问题就会变得麻烦,所以再求右边边界值的时候我们用lower,即找第一个>=x+y的数,这样一来r-1就一定是最后一个比x+y小的值的下标,同时我们sum+=的时候加的是r-l,这样一来就刚好是这个区间内元素的个数,因为这个区间就是[l,r).
2.为什么upper和lower的时候起始点都是a + i + 1
而不是a + 1
?
因为会有重复
如果有一下一组数据
7 30
15 16 27 35 42 51 92(顺序已经排好)
如果我们已知两边分别为30和16的话,我们应该找位于区间(14,46)的元素的个数,第一个数字15显然在这个区间中,但是如果按照代码从a + i + 1
开始找的话不就找不到15了吗?很明显不会的,因为30 16 15这个组合在找30 15的时候就找完了,也就是说其实并不是30和16找15而是30和15找的16,因为15的位置比16靠前,所以这个组合在之前就已经被找过了,如果我们从a+1
开始找的话,那15 16 30这个组合就被找过两次了,所以我们从a+i+1
开始找,这样可以避免找到重复的组合。
3.为什么upper和lower的时候起始点都是a + i + 1
而不是a + i
?
因为a+i+1
把a+i
这个点排除在外了
例如一个序列 15 16 27 ,我们要已知两边为30和16,那么我们要找的区间就是(14,46),很显然16本身就在这个序列中,但是因为16已经作为一条边出现了,所以我们不能再考虑16,我们需要筛掉它,所以当我们遍历到第i个点时要从a+i+1
开始,而不是从a+i
开始