题目链接
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;
}