D. Pair of Topics


D. Pair of Topics


标签

  • 思维

简明题意

  • 给定两个n长数组a和b。需要找出满足 i < j , a i + a j < b i + b j i<j,a_i+a_j<b_i+b_j i<j,ai+aj<bi+bj的数量。

思路

  • 我们移项一下,发现是要找 i < j , a i − b i < − ( a j − b j ) i<j,a_i-b_i<-(a_j-b_j) i<j,aibi<(ajbj)
  • 我们令c[]数组为a数组和b的差。上式就变成 i < j , c i < − c j i<j,c_i<-c_j i<j,ci<cj
  • 那么很自然的想法,就是在每一个c[i]后面找值小于 − c i -c_i ci的数的数量。这样想,相当于一个在线算法,做法是:权值线段树维护值在某个范围内的数量有多少个。在我们一依次向后考虑每个i的时候,就把前面的a[i]从权值线段树中删掉。然后注意一下要把负数偏移一下,a[i]是1e9的还要离散化一下。很复杂
  • 考虑离线做法。 按照 i < j , c i < − c j i<j,c_i<-c_j i<j,ci<cj这个式子想,我们就进入误区了。我们实际可以发现,可以这样求: i < j , c i + c j < 0 i<j,c_i+c_j<0 i<j,ci+cj<0的对数。这样的话,就和位置无关了,这实际找的就是数列中和<0的对数,那么我们可以随意打乱数组的顺序。
  • 那么就可以给数组排序,对每一个数,在它后面寻找和这个数<0的数有多少。
  • 这里就有两种做法了,我们可以枚举每一个数,二分找。
  • 注意到我们枚举的数是依次递增的,这就类似二维偏序。假设有两个集合,一个是所有的枚举了的数,一个是还没有被枚举的数,那么枚举了的数越大,相应备选的数就越少,而且是在原来基础上越少。 (口胡的忽略就好) 然后就双指针呗,一直往前就好(我觉得双指针是一种二维偏序,或者说很相似)

注意事项


总结

  • 搞清楚lower_bound()-a中-a得到的是啥。不管lowerbound中第一个参数是a加多少,-a的到的都是:找到的那个元素的下标。
  • 题目要求二元组,要是有下标限制,那么我们可以试着转换一下,让其没有下表限制,从而离线做。

AC代码

二分做法

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring> 
#include<stack>
#include<map>
#include<queue>
#include<cstdio>	
#include<set>
#include<map>
#include<string>
using namespace std;

const int maxn = 2e5 + 10;

int a[maxn];

void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)
	{
		int t;
		cin >> t;
		a[i] -= t;
	}

	sort(a + 1, a + 1 + n);

	long long ans = 0;
	for (int i = 1; i <= n; i++)
		ans += n -(upper_bound(a + i + 1, a + 1 + n, -a[i]) - a) + 1;
	cout << ans;
}
	
int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

双指针做法

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring> 
#include<stack>
#include<map>
#include<queue>
#include<cstdio>	
#include<set>
#include<map>
#include<string>
using namespace std;

const int maxn = 2e5 + 10;

int a[maxn];

void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)
	{
		int t;
		cin >> t;
		a[i] -= t;
	}

	sort(a + 1, a + 1 + n);

	long long ans = 0;
	int r = n;
	for (int i = 1; i <= n; i++)
	{
		while (a[i] + a[r] > 0)
			r--;
		ans += min(n - r, n - i);
	}
	cout << ans;
}
	
int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值