@3013216027
2019-09-20T14:53:47.000000Z
字数 1503
阅读 459
未分类
小明:
小红:
假设,
若,,
此时二者的区间分别是:
令:
即只需要满足,就可以达到要求。
若,,且
此时二者的区间分别是:
令:
即需要满足。
综上,若,则需要满足
类似地,可以得出,,三种情形的结果。综合起来就是:
只需要落在以下区间内即可:
因此,可以遍历,然后求出和上述区间的4个交点(假设从下往上依次为),然后计算原数组中有多少个元素满足或者,累加起来就是总个数。当然,还有2点需要注意:
- 第1/3象限的区块包含了这条直线,而题目中还隐含了的条件,所以这条直线上的点需要排除掉
- 4个区块关于对称,这会导致对任意的,也必然落在区间内,所以最后求出的结果需要除以2
下面考虑对任意的,怎么求原数组中满足的个数:只需要将数组排序,求出第一个的下标和最后一个的下标,结果就是。用二分就可以了。
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long ll;const int MAX = 200019;ll a[MAX];int n;// find num of elements in range [low, high]int find_range(ll low, ll high, ll target) {if (low > a[n-1] || high < a[0]) {return 0;}int lb = lower_bound(a, a + n, low) - a;int ub = upper_bound(a, a + n, high) - a - 1;// printf("find_range(%d,%d)=%d\n", low, high, ub - lb);if (a[lb] <= target && target <= a[ub]) {return ub - lb < 0 ? 0 : ub - lb;}return ub - lb + 1;}int main() {while (~scanf("%d", &n)) {for (int i = 0; i < n; i++) {scanf("%lld", a + i);}sort(a, a + n);ll sum = 0LL;for (int i = 0; i < n; i++) {if (a[i] < 0) {sum += find_range(a[i]<<1, a[i]-1>>1, a[i]);sum += find_range(-a[i]-1>>1, -a[i]<<1, a[i]);} else {sum += find_range(-a[i]<<1, -a[i]-1>>1, a[i]);sum += find_range(a[i]+1>>1, a[i]<<1, a[i]);}}printf("%lld\n", sum>>1);}return 0;}