洛谷——P1102 A-B数对

最近刚刚学了堆排序,这个题派上用场了,当然这里也可以使用快排。

【题目描述】

给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

要求:

对于 75% 的数据,1≤N≤2000。

对于 100% 的数据, 1≤N≤2×10^{5},0≤a_{i}​<2^{30},1≤C<2^{30}

【输入】

输入共两行。

第一行,两个正整数 N,C。

第二行,N 个正整数,作为要求处理的那串数。

【输出】

一行,表示该串正整数中包含的满足 A−B=C 的数对的个数。

样例输入

4 1
1 1 2 3

样例输出

3

样例输入

10 3
10 4 7 5 10 4 5 8 5 7

样例输出

11

解题思路

 这个题直接用暴力解不可行,因为数据太多了,而且存储的类型不能用 int,得用 long long。

我们需要想一个可以节省时间的方法,毕竟一个一个遍历行不通。

所以可以试试二分查找,当然这个方法也可以过。

首先,看题目,是找 A-B=C 的数对个数,那么可以转化成 A=B+C,用一个循环遍历一遍所有数字找符合当前的 a[i]=a[x]+C。

怎么使用这个二分查找呢?

当然在二分查找之前要排序,否则肯定是错误的。

这里我用的是 C 语言的堆排序(通过最大堆实现,每找到最大值就与第一个数交换,再重新向下调整),其实用快排也可以,当然要是直接用 C++ 的库函数更方便。

可以用一个变量 k 存储 B+C 的值,初始化 head=i+1,tail=n,然后二分查找小于 k 的位置在哪,也就是 tail(尾部)的位置,用一个 s 记录下来,接着初始化 head=i+1,tail=n,再使用一次二分查找小于等于 k 的位置,此时 tail 与 s的差为满足要求的数对个数。用计数器 sum 统计。

代码如下:(有点长,但是不难理解)

#include
long long a[200005],n;
//交换以 x为下标和以 y为下标的数组的值 
void swap(int x,int y)
{
	int t;
	t=a[x];
	a[x]=a[y];
	a[y]=t;
	return ;
}
//向下调整函数(最大堆) 
void siftdown(int i)
{
	int flag=0,t;
	while(flag==0&&i*2<=n)//首先满足需要继续调整的条件,而且至少存在左儿子 
	{
		if(a[i]0){
		swap(1,n);
		n--;
		siftdown(1);
	}
	return ;
}
void creat()
{
	int i;
	//建堆可以不从叶结点开始,因为每个叶结点没有儿子节点,以当前的叶结点为根结点,是一定满足最大堆的条件的 
	for(i=n/2;i>=1;i--)
	siftdown(i);
	return ;
}
int main()
{
	long long num,c,i;//定义long long类型 
	scanf("%lld %lld",&n,&c);
	num=n;//这里不能省,因为通过堆排序的最大值出堆,要更新堆中元素的个数 
	for(i=1;i<=n;i++)
	scanf("%lld",&a[i]);//输入 
	creat();//建堆 
	heap();//将最大值放在最后,并且与最后一个数交换,接着向下调整 
	//以上是堆排序 
	//以下是二分查找 
	long long s,k,mid,sum=0,head,tail;
	for(i=1;i<=num;i++)//遍历数组元素 
	{
		k=a[i]+c;//如果要满足条件,a[x]==k 
		head=i+1,tail=num;//初始化
		//找小于 k的部分 
		while(head<=tail)
		{
			mid=(head+tail)/2;
			if(a[mid]

你可能感兴趣的:(题组,算法,数据结构,c++)