AT5252题解

题目解释

\(a\) 数组中找到 \(a_i\)\(a_j\)\(a_k\),满足

\(\begin{cases}a_i+a_j=a_k\\a_i+a_k=a_j\\a_j+a_k=a_i \end{cases}\)

中的任意一条。

前置芝士:lower_bound

lower_boundC++ 自带的二分函数,时间复杂度 \(\mathcal O (\log n)\),食用方法是:

lower_bound(一个指针,另一个指针,a)

其中这两个指针都在同一个数组里,

它的用处是在第一个指针所指向的数(包括)到第二个指针之间所指向的数中(不包括)第一个大于 \(n\) 的数,这两个指针之间的子序列必须是单调不下降的,并返回这个数的地址

使用示例:

int a[]={1,2,3,4,5,6,9,10,1000};
cout<<lower_bound(a,a+n,3)-a;//会输出2

拓展:关于指针

对于一个数组 a[]a[0] 的地址可以表达为 \(a\),即

int a[100];
cout<<&a;

会输出 a[0] 的地址。

而且数组的地址是连续的,所以 a[i] 的地址可以表达为 \(a+i\)

思路 1

直接暴力枚举 \(i\)\(j\)\(k\),但由于 \(n \le 2\times10^3\),时间复杂度 \(\mathcal O (n^3)\),必定超时。

思路 2

先对数组 \(a\) 排序,然后枚举 \(i\)\(j\),然后在 \([ j+1,n)\) 找到最后一个 \(<i+j\) 的,再将 \(ans+\text{那个数的位置} - j + 1\),具体看下面:

1 2 3 4 5 6 7 10 12 15 17
0 0 0 0 i j Y Y  N  N  N

我们观察 \(>j\) 的这一段,具有单调性。

所以二分就行。

code

sort(a,a+n);
for(int i=0;i<n-2;i++){
   for(int j=i+1;j<n-1;j++){
       int k=lower_bound(a+j+1,a+n,a[i]+a[j])-a;
       ans+=(k-j-1);
   }
}
cout<<ans;

注意&提示

  • 数组开大一点;
  • lower_bound 第一个参数是 \(a+j+1\)
  • 循环范围:\(i<n-2\)\(j<n-1\)
posted @ 2021-08-06 14:38  WiccldCute  阅读(144)  评论(1)    收藏  举报