题意:已知n个数a[i] 。给定区间[l,r] 。 m次询问,求得在[l,r]区间内的素数中,可以整除a[i]的所有a[i]个数和。例如 2 3 4 5 6区间[2,6] ,素数仅有3,5。可以被3整除的有3、6,可以被5整除的有5.那么累加和是3。
数据分析:1 ≤ n ≤ 1e6 :: 2 ≤ a[i]≤ 107 :: 1 ≤ m ≤ 50000 :: 2 ≤ l≤ r ≤ 2·e9
思路:刚开始想就是暴力,一看复杂度,不对,肯定要预处理后再暴力。那么如何预处理呢? 就是要求出[2,max]的前缀和。正好又可以通过素数筛的时候,遍历 以当前i为素因数的所有数字。累加。然后再求前缀和
复杂度分析:O(max)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+70;
int cnt[maxn];
ll sum[maxn];
bool isprime[maxn];
int mmax=INT_MIN;
void solve()
{
memset(sum,0,sizeof(sum));
memset(isprime,true,sizeof(isprime));
isprime[0]=isprime[1]=false;
for(int i=2;i<=mmax;i++)// 在求素数的同时求得当前素数i对应倍数的个数和。
{
if(isprime[i])
{
sum[i]+=cnt[i];
for(int j=i+i;j<=mmax;j+=i)
{
sum[i]+=cnt[j];
isprime[j]=false;
}
}
}
for(int i=3;i<=mmax;i++)
sum[i]=sum[i-1]+sum[i];
}
int main(void)
{
int n;
cin >> n;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
cnt[x]++; // 记录x出现次数
mmax=max(mmax,x); // 求最大值
}
solve(); //求前缀和
int m;
cin >> m;
while(m--)
{
ll l,r;
scanf("%I64d%I64d",&l,&r);
if(r<=mmax)
{
printf("%I64d\n",sum[r]-sum[l-1]);
}
else if(l<=mmax && r>mmax)
{
printf("%I64d\n",sum[mmax]-sum[l-1]);
}
else if(l>mmax) printf("%d\n",0);
}
return 0;
}