【codeforces】gym 101741 A - Three Arrays(二分做法)

本文解析 CodeForces 题目A,介绍一种高效算法来计算满足特定条件的三元组数量。通过枚举数组元素并使用二分查找,避免了重复计算的问题。注意处理边界条件,确保计算准确无误。

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

 题目链接

http://codeforces.com/gym/101741/problem/A

 

题目大意

给定三个序列 a,b,c  都是从小到大依次排列 然后问你有多少个三元组 (i,j,k) 满足
| a[i]- b[j] | ≤ d,  | a[i] - c[k] | ≤ d,   | b[j] - c[k] | ≤ d.

序列长度最长为  5e5

 

思路

真是这套题里最水的一道了orz……即使这样也想了蛮久的orz

首先,在三个数组中选一个枚举,被枚举的那个数组中的每个元素看作三元组中的最小元素

假设k是被扫的数组中的元素大小
这样另外两个数组都是寻找 [k, k+d] 范围内的数字,两次二分查找
因为后两个数组是分步操作,所以用 乘法原理 乘起来
用这样的方法枚举三个数组就分别得到了三个数组中各自元素为最小时的所有情况,加起来就是答案。

--------------------------华丽的分割线----------------------------------

但是这样扫其实是会产生问题的,比如如果是

数组1中元素=数组2中元素  (a[i]= b[j]

这种情况,在我们枚举数组1和数组2的时候就会重复加
解决办法是,我们设定一个偏序。

比如扫数组1的时候,数组2和数组3的元素必须严格大于数组1元素
扫数组2的时候,数组1元素可以等于数组2元素,数组3元素必须严格大于数组2元素
扫数组3的时候,1和2都可以取等号

 

还有就是要注意lower_bound 或者 upper_bound 返回的指针先不要直接减,可能会有溢出,应该先把值存起来转long long

上代码~

#include <iostream>
#include <algorithm>
using namespace std;
long long d, n[3], a[3][500005];
int main()
{
    while (cin>>d)
    {
        for (int i=0; i<3; i++)
            scanf("%I64d", &n[i]);
        for (int i=0; i<3; i++)
            for (int j=0; j<n[i]; j++)
                scanf("%I64d", &a[i][j]);
        long long sum=0;
        long long *p1, *p2, *p3, *p4;
        for (int j=0; j<n[0]; j++)  //枚举第一个数组
        {
            p1=upper_bound(a[1], a[1]+n[1], a[0][j]+d);  // 在数组2里找 [k, k+d]
            p2=upper_bound(a[1], a[1]+n[1], a[0][j]);
            p3=upper_bound(a[2], a[2]+n[2], a[0][j]+d); // 在数组3里找 [k, k+d]
            p4=upper_bound(a[2], a[2]+n[2], a[0][j]);
            sum+=(long long)1*(p1-p2)*(p3-p4);
        }
        for (int j=0; j<n[1]; j++)  //枚举第二个数组
        {
            p1=upper_bound(a[0], a[0]+n[0], a[1][j]+d); // 在数组1里找 (k, k+d]
            p2=lower_bound(a[0], a[0]+n[0], a[1][j]);
            p3=upper_bound(a[2], a[2]+n[2], a[1][j]+d); // 在数组3里找 [k, k+d]
            p4=upper_bound(a[2], a[2]+n[2], a[1][j]);
            sum+=(long long)1*(p1-p2)*(p3-p4);
        }
        for (int j=0; j<n[2]; j++)  //枚举第三个数组
        {
            p1=upper_bound(a[1], a[1]+n[1], a[2][j]+d); // 在数组2里找 (k, k+d]
            p2=lower_bound(a[1], a[1]+n[1], a[2][j]);
            p3=upper_bound(a[0], a[0]+n[0], a[2][j]+d); // 在数组1里找 (k, k+d]
            p4=lower_bound(a[0], a[0]+n[0], a[2][j]);
            sum+=(long long)1*(p1-p2)*(p3-p4);
        }
        cout<<sum<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值