标签
- 思维
简明题意
- 给定两个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,ai−bi<−(aj−bj)
- 我们令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;
}