7-8 三足鼎立 (25 分)

本文详细解析了一道C++竞赛题目,重点介绍了如何利用upper_bound和lower_bound函数寻找有效组合的方法,通过具体示例阐述了边界条件的选择及其背后的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接(这个比赛已经结束了没法提交了,只能用来看题面,暂时没找到可以提交的网站)

#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+1a+i这个点排除在外了
例如一个序列 15 16 27 ,我们要已知两边为30和16,那么我们要找的区间就是(14,46),很显然16本身就在这个序列中,但是因为16已经作为一条边出现了,所以我们不能再考虑16,我们需要筛掉它,所以当我们遍历到第i个点时要从a+i+1开始,而不是从a+i开始

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值