CF - C. A Tale of Two Lands(绝对值,双指针/尺取)

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 2n2105, 109ai109

思路
一开始的想法就是化公式,分正负讨论,后面还要二分找区间,然后就很麻烦。。

正解是需要找到性质:
发现对于 {-3,5} 和 {3,5} 两个数对,得到的 ∣ x ∣ + ∣ y ∣ |x|+|y| x+y ∣ ∣ x ∣ − ∣ y ∣ ∣ ||x| - |y|| ∣∣xy∣∣ 是相同的,那么就说明绝对值相同的两种数对,会得到相同的区间。
无论 x 和 y 的正负,|x+y| 和 |x-y| 都是 ∣ x ∣ + ∣ y ∣ |x|+|y| x+y ∣ ∣ x ∣ − ∣ y ∣ ∣ ||x| - |y|| ∣∣xy∣∣

所以就不需要考虑正负,直接取绝对值。

将所有值按照绝对值从小到大排序,对于数对 { 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| ∣∣xy∣∣=yx 要满足在左端点 |x| 的左侧,需要 ∣ y ∣ − ∣ x ∣ < ∣ x ∣ |y| - |x| < |x| yx<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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值