https://codeforces.com/contest/1166/problem/C
题意
给定长度为 n 的数列
a
[
]
a[]
a[],问其中一共有多少个数对
{
i
,
j
}
\{i, j\}
{i,j},其对应值
x
,
y
x,y
x,y 满足:
- 端点分别为 |x| 和 |y| 构成的区间完全包含于端点分别为 |x+y| 和 |x-y| 构成的区间。
2 ≤ n ≤ 2 ⋅ 1 0 5 , − 1 0 9 ≤ a i ≤ 1 0 9 2 \le n \le 2 \cdot 10^5,\ −10^9≤a_i≤10^9 2≤n≤2⋅105, −109≤ai≤109
思路
一开始的想法就是化公式,分正负讨论,后面还要二分找区间,然后就很麻烦。。
正解是需要找到性质:
发现对于 {-3,5} 和 {3,5} 两个数对,得到的
∣
x
∣
+
∣
y
∣
|x|+|y|
∣x∣+∣y∣ 和
∣
∣
x
∣
−
∣
y
∣
∣
||x| - |y||
∣∣x∣−∣y∣∣ 是相同的,那么就说明绝对值相同的两种数对,会得到相同的区间。
无论 x 和 y 的正负,|x+y| 和 |x-y| 都是
∣
x
∣
+
∣
y
∣
|x|+|y|
∣x∣+∣y∣ 和
∣
∣
x
∣
−
∣
y
∣
∣
||x| - |y||
∣∣x∣−∣y∣∣。
所以就不需要考虑正负,直接取绝对值。
将所有值按照绝对值从小到大排序,对于数对
{
i
,
j
}
(
i
<
j
)
\{i, j\}(i<j)
{i,j}(i<j)来说,就能保证
∣
x
∣
|x|
∣x∣ 为构成区间的左端点,
∣
y
∣
|y|
∣y∣ 为区间右端点。
那么
∣
x
∣
+
∣
y
∣
|x|+|y|
∣x∣+∣y∣ 一定在区间右端点的右侧,不用管;
而
∣
∣
x
∣
−
∣
y
∣
∣
=
∣
y
∣
−
∣
x
∣
||x| - |y|| = |y| - |x|
∣∣x∣−∣y∣∣=∣y∣−∣x∣ 要满足在左端点 |x| 的左侧,需要
∣
y
∣
−
∣
x
∣
<
∣
x
∣
|y| - |x| < |x|
∣y∣−∣x∣<∣x∣,即
∣
y
∣
<
2
∣
x
∣
|y| < 2|x|
∣y∣<2∣x∣。
所以,对于位置 i
后面的位置 j
,只要满足 a[j] < 2*a[i]
,那么数对 {i, j}
就是满足的。
而对于 i
从前往后走,满足的最后一个 j
位置也是单调往后走的,所以可以用一个指针不断往后指。
对于每个位置 i
,每次找到最后满足的位置 j
,和当前位置之差便是满足的以 i
为左的点对个数,累加求和。
Code
#include<bits/stdc++.h>
using namespace std;
#define Ios ios::sync_with_stdio(false),cin.tie(0)
const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N];
signed main(){
Ios;
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i], a[i] = abs(a[i]);
sort(a+1, a+n+1);
int j = 0, ans = 0;
for(int i=1;i<=n;i++)
{
while(j + 1 <= n && a[j + 1] - a[i] <= a[i]) j ++;
ans += j - i;
}
cout << ans << endl;
return 0;
}