Description
给定 n n n个数,从中选出三个数,使得最大的那个减最小的那个的值小于等于 d d d,问有多少种选法。
Input
第一行两个整数 n , d n,d n,d
第二行 n n n个整数满足 a i a_i ai,数据保证a单调递增。
( 1 ≤ n ≤ 1 0 5 , 1 ≤ d ≤ 1 0 9 , ∣ a i ∣ ≤ 1 0 9 ) (1\le n\le 10^5,1\le d\le 10^9,|a_i|\le 10^9) (1≤n≤105,1≤d≤109,∣ai∣≤109)
Output
输出一个整数表示满足条件的选法。
Sample Input
4 3
1 2 3 4
Sample Output
4
Solution
给 a a a序列升序排,考虑所选最小值为 a i a_i ai时的方案数,令 f i f_i fi为满足 a f i − a i ≤ d a_{f_i}-a_i\le d afi−ai≤d的最大编号,那么另外两个值从 [ a i + 1 , a f i ] [a_{i+1},a_{f_i}] [ai+1,afi]中选取即可,方案数 C f i − i 2 C_{f_i-i}^2 Cfi−i2,注意到 f i f_i fi随 i i i的增加不减,故直接游标法维护 f i f_i fi即可,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
Code
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=100005;
int n,d,a[maxn];
int main()
{
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
ll ans=0;
for(int i=1,pos=1;i<=n;i++)
{
while(pos<=n&&a[pos]-a[i]<=d)pos++;
pos--;
ans+=(ll)(pos-i)*(pos-i-1)/2;
}
printf("%lld\n",ans);
return 0;
}