这几天在做CF,DIV2的题大多是YY题,不过感觉这里的题比较有技巧,比较新颖,挺好玩的。。
题意:给定一个数轴,有n个不同的点,求任取3个点使这3个点的两两距离都不超过d;
思路:二分就可以了,可以当作是组合数学来做。。
枚举每个点,然后二分找出以当前点为起点所能构成的最大区间,然后再减去与上个
点重复的那部分;
如n=5,d=3;Xi=1,2,3,4,5;则枚举1时,以1为起点所能构成的区间包含的点
为1,2,3,4;有3种情况满足条件res+=3;枚举以2为起点时,则以2为起点所能
构成区间包含的点为2,3,4,5,则res+=3;但(2,3,4)这个可能重复了要减去1,
以此类推;
//author Joy
#pragma comment(linker, "/STACK:66777216")
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#define LL __int64
using namespace std;
typedef struct coor
{
int x,y;
};
int nomalDay[]={0,31,28,31,30,31,30,31,31,30,31,30,31};//平年
int leapDay[]={0,31,29,31,30,31,30,31,31,30,31,30,31};//闰年
const int ARRSIZE=100100;
const int STRSIZE=100100;
const int GRIDSIZE=510;
const int MAXINF=(2<<20);
const int MININF=(~(2<<20));
inline bool upcmp(int a,int b)
{
return a<b;
}
inline bool downcmp(int a,int b)
{
return a>b;
}
/*---------------分割线---------------*/
int num[ARRSIZE];
LL fac(LL n) //谨记:当中间结果超过int的时候要用long long,因为这里WA两次,TAT。。
{
if(n<3)
return 0;
return (n*(n-1)*(n-2))/6;
}
int bs(int a[], int l, int h, int v)
{
int m;
while ( l < h )
{
m = ( l + h ) >> 1;
if (a[m] < v) l=m+1;
else h=m;
}
return l;
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
int n,d;
while (cin>>n>>d)
{
int i,last=1;
LL res=0;
for (i=1;i<=n;i++)
scanf("%d",&num[i]);
for(i=1;i<=n;i++)
{
int idx=bs(num,i,n,num[i]+d);
if(num[idx]>num[i]+d)
idx--;
res+=fac(idx-i+1);
res-=fac(last-i+1);
last=idx;
}
cout<<res<<endl;
}
return 0;
}