今天做了一个二分的题目,自己独立做出来的,真不敢相信,调试了好几遍,虽然也看数据了,但是从头到尾没看题解。
只能说y总的板子太厉害了,他的二分我看了好几遍才看懂,看懂之后就很简单了。
题目:
洛谷1102 A-B数对
好吧,题目是这样的:给出一串数以及一个数字C, 要求计算出所有A-B=C的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个整数 N, C。
第二行,NN 个整数,作为要求处理的那串数。
输出格式
一行,表示该串数中包含的满足 A - B = C 的数对的个数。
解题思路:
在原序列找到A,B使得A-B=C,如果是暴力两层for指定会超时,公式变形:A=B+C; 也就是枚举原序列的每一个B
如果在原序列中能找到B+C这个值(找到几个计数加几),否则不加。
那找B+C这个值我们可以用二分啊,时间会快些。
二分B+C这个值的话,我们先对数组升序,然后二分。其实升序就是在制造一个性质,使得B+C作为这个性质的边界点。
左边都小于他,右边都大于他,接下来是代码,慢慢写,理解好每一步,你也可以的。
代码
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<math.h>
using namespace std;
const int N=1000010;
int q[N];
int main()
{
int n,c;
cin>>n>>c;
for(int i=0;i<n;i++)
cin>>q[i];
long long cnt=0;
sort(q,q+n);
//枚举每一个值B,看是否能找到B+C这个值,找到就是一对儿了!
for(int i=0;i<n;i++)
{
//二分q[i]+c的起始位置
int l=0,r=n-1;
while(l<r)
{
int mid =l+r >>1;
if(q[mid]>=q[i]+c)r=mid;
else l=mid+1;
}
int x1=l;
if(q[l]!=q[i]+c)continue;
else
{//二分q[i]+c最后一个位置
l=0,r=n-1;
while(l<r)
{
int mid= l+r+1 >>1;
if(q[mid]<=q[i]+c)l=mid;
else r=mid-1;
}
int x2=l;
cnt+=x2-x1+1;
}
}
cout<<cnt<<endl;
return 0;
}
总结
1.首先得发现能二分的点,我觉得这个很重要,这个如果理解了,那么就是真的懂二分了。
单调性的问题可以用二分解决,不是单调性的问题有的也可以用二分解决。主要是看是否能找到一个
性质使得,左边满足一个性质,右边满足一个性质。然后我们用二分来确定这个点
二分的步骤:
1.首先确定二分哪个值,以及答案所在的范围l,r
2.画图,想二分起始位置呢,还是二分最后一个位置呢?
3.用y总模板就行。(一定要深刻理解每一个步骤)
//还有许多需要注意的点:
//就比如说:while(l<r)结束条件一定是l==r,二分是一定有结果的,但是二分出来的不一定是目标值,对吧。
//根据题目要求你要做适当的判断。
//我觉得这道题应该就是有效的刷题,把学到的原理,以及学到的板子,以及就相应的问题思考分析,最后得到总结,有时候会没有思路,有时候也需要看题解,我认为这个过程中自己要真正弄懂怎么回事,以及学到点什么才是有价值的。
最后希望自己在任何时候都能坚定自己一直努力做的事情,看到自己的态度和决心!
加油