xay loves count
题目大意,给定一个序列,求其中有多少满足ai*aj=ak的可能性!
思路:n的范围最大是1e6,要是暴力的话复杂度是n^3,明显不行!
ai范围也在1e6,因此很容易想到当可以通过空间复杂度来优化时间复杂度!!即运用vis判断ai*aj的值是否存在!!那么此时我们将复杂度优化到了n^2(只需要循环i与j)。但显然还会超时!
接下来是最重要的,发现对任意数列,a1,a2,a3…an, 在循环i的时候只需要到sqrt(an),往后i,j只是重复出现,因此复杂度就能优化到O(n*sqrt(n)),即可满足复杂度!!!
其实就是两步简化的过程!!!上代码!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll vis[N];
ll cnt[N];
int main(){
memset(vis,0,sizeof(vis));
ll n,pos=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++){
ll a;
scanf("%lld",&a);
if(!vis[a]) cnt[++pos]=a;
vis[a]++;
}
ll res=0;
sort(cnt+1,cnt+pos+1);//排序
ll m=sqrt(cnt[pos]);//找到需要循环到的数的最大值
ll mpos=lower_bound(cnt+1,cnt+pos+1,m)-cnt;//找出i需要循环到的下标
for(int i=1;i<=mpos;i++){
for(int j=i;j<=pos;j++){
if(cnt[i]*cnt[j]>cnt[pos]) break;//剪枝 ,不加会下标越界
if(vis[cnt[i]*cnt[j]]){
//cout << cnt[i] << ' ' << cnt[j] << ' ' << cnt[i]*cnt[j] << endl;
if(i==j) res=res+vis[cnt[i]]*vis[cnt[j]]*vis[cnt[i]*cnt[j]];
else//i!=j,i和j可以有两种情况 res=res+vis[cnt[i]]*vis[cnt[j]]*vis[cnt[i]*cnt[j]]*2;
}
}
}
printf("%lld\n",res);
return 0;
}