Codeforces Round #725 (Div. 3) C.Number of Pairs

主要思路:

看到t和n的数据范围就知道暴力是不可能的啦!

那暴力不行我们就二分:

先对数组进行一次排序;

然后从后往前扫,找到一个a[i]就进行两次二分操作

第一次二分查找最大的up值,使得a[up]+a[i]<=r;

第二次二分查找最小的down值,使得l<=a[down]+a[i];

up-down的值就是a[i]组成的数对的个数。

关键来了,我当时已经想到了这个思路,但是花了足足50分钟竟然写不出来这个二分!!(;へ:)

后来,结束的时候去看看大哥们的代码,发现了这个东西:

ans+=upper_bound(a,a+i,r-a[i])-lower_bound(a,a+i,l-a[i]);

我当时人都傻了,这是什么玩意?!!!∑(゚Д゚ノ)ノ

后来我查了一下:

lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。

在从小到大的排序数组中,

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
(上面截取CSDN博主「brandong」的原创文章,原文链接:https://blog.csdn.net/qq_40160605/article/details/80150252)

哇,好东西,赶紧用起来,于是我也AC了

#pragma GCC optimize(2)
#include<map>
#include<queue>
#include<math.h>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define IOS ios::sync_with_stdio(false);
#define f(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rp(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(a,i) memset(a,i,sizeof(a));
#define die {cout<<"-1"; return 0;}
#define debug cout<<"0_0"<<endl
#define sd(a) scanf("%d",&a)
#define slld(a) scanf("%lld",&a)
#define sdd(a,b) scanf("%d %d",&a,&b)
const double eps=1e-8;
const ll INF=1e18;
const int max1=1e9;
const double pie=acos(-1.0);
using namespace std;
// ^_^
int a[200005];
int main()
{
    int t;
    sd(t);
    while(t--)
    {
        int n,l,r;
        sd(n);
        sdd(l,r);
        f(i,0,n-1)
			sd(a[i]);
        sort(a,a+n);
        ll ans=0;
        rp(i,n-1,0)
        {
            if(a[i]>=r)
				continue;
            if(a[i]>=l)
                ans+=upper_bound(a,a+i,r-a[i])-a;
				//从a[0]到a[i-1]中二分找到不超过 r-a[i]的最大下标
            else
            {
                ans+=upper_bound(a,a+i,r-a[i])-lower_bound(a,a+i,l-a[i]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值