@3013216027
2019-09-20T14:53:47.000000Z
字数 1503
阅读 390
未分类
小明:
小红:
假设,
若,,
此时二者的区间分别是:
令:
即只需要满足,就可以达到要求。
若,,且
此时二者的区间分别是:
令:
即需要满足。
综上,若,则需要满足
类似地,可以得出,,三种情形的结果。综合起来就是:
只需要落在以下区间内即可:
因此,可以遍历,然后求出和上述区间的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;
}