我最初的时候枚举起点x,然后二分查找数值为x*k区间中的位置位于x之后的区间,然后在枚举区间的y(=x*k),用二分再求出y*k切在其之后的区间
这样写时间会t
后面发现枚举起点效率低,没有枚举中点效率高,因为枚举中点一可以剔除x%k!=0的
并且可以在lgn的时间内 查找到x/k和x*k的区间长度
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2e5+100;
long long n,k,ans;
struct node{
long long num;
int pos;
node(long long num,int pos):num(num),pos(pos){}
node():num(0),pos(0){
}
bool operator < (const node& rhy)const{
return num<rhy.num||(num==rhy.num&&pos<rhy.pos);
}
}a[maxn];
long long findcnt(long long x,int s,int e){
return upper_bound(a,a+n,node(x,e-1))-lower_bound(a,a+n,node(x,s+1));
}
int main(){
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>a[i].num;
a[i].pos=i;
}
ans=0;
sort(a,a+n);
if(n>=3){
for(int i=0;i<n;i++){
long long num=a[i].num;
int pos=a[i].pos;
if(num%k==0)
ans+=findcnt(num/k,-1,pos)*findcnt(num*k,pos,n+1);
}
}
cout<<ans<<endl;
return 0;
}
看别人代码发现更加机智的写法,用两个map分别记录下第i位之前和之后的每个数字出现次数
然后~~~~~
map<int,int> s, t;
rep(i, n) ++ t[a[i]];
ll ans = 0;
rep(i, n) {
int x = a[i];
-- t[x];
if(x % k == 0 && abs((ll)x * k) <= (ll)1e9)
ans += (ll)s[x / k] * t[x * k];
++ s[x];
}