题意:已知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;
}