传送门:http://codeforces.com/contest/691/problem/F
题意:求序列中乘积大于等于p的点对的个数
刚开始还想用数据结构维护一下,想想不靠谱
先反向思考求乘积小于p的情况,具体的有两种思路
思路一:类似于埃式筛法,cnt[a[i]]记录a[i]的个数,mul[i]记录点对乘积是i*j的个数
复杂度:O(NloglogN+m)
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll __int64
const int N=3e6+10;
int cnt[N], a[N], p[N];
ll mul[N];
int main(){
int n, m;
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
cnt[a[i]]++;
}
scanf("%d", &m);
for(int i=1; i<=m; i++)scanf("%d", &p[i]);
for(int i=1; i<N; i++){
if(!cnt[i])continue;
for(int j=i; j<N; j+=i){//用埃式筛法的思想处理乘积问题
mul[j]+=1LL*cnt[i]*(cnt[j/i]-(j/i == i));
/* if(j/i!=i)mul[j]+=1LL*cnt[i]*cnt[j/i];
else mul[j]+=1LL*cnt[i]*(cnt[i]-1); //mul[j]表示乘积为j的个数*/
}
}
for(int i=1; i<N; i++)mul[i]+=mul[i-1];
for(int i=1; i<=m; i++){
ll ans=1LL*n*(n-1)-mul[p[i]-1]; //不要忘记LL
printf("%I64d\n", ans);
}
return 0;
}
思路二:
先去重再离散化,这时候a[i]表示的下标,按下标枚举去重后的数组并且点对的乘积小于p,这样子均摊的复杂度还是O(N)
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll __int64
const int N=3e6+10;
int cnt[N], a[N], b[N];
ll mul[N];
int main(){
int n, m;
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
b[i]=a[i];
}
scanf("%d", &m);
sort(b+1, b+n+1);
int tol=unique(b+1, b+n+1)-(b+1);//离散化
for(int i=1; i<=n; i++)
a[i]=lower_bound(b+1, b+tol+1, a[i])-b;
for(int i=1; i<=n; i++)cnt[a[i]]++;
ll all=1LL*n*(n-1);
n=tol;
for(int i=1; i<=n; i++){//复杂度大概O(sqrt(n)*sqrt(n))
for(int j=1; j<=n; j++){
if(b[i]*b[j]<N) mul[b[i]*b[j]]+=cnt[i]*(cnt[j]-(i==j));
else break;
}
}
for(int i=1; i<N; i++)mul[i]+=mul[i-1];
while(m--){
int p;
scanf("%d", &p);
printf("%I64d\n", all-mul[p-1]);
}
return 0;
}