牛客小白月赛39 G 线性筛素数+离线+树状数组
题目链接
题意
有q(q<=3e6)组询问,每次给你n和k(n,k<=3e6),你需要回答在小于等于n的数中有多少个数可以表示为一些>=k的质数的乘积
思路
首先,由于q很大,n也很大,可以发现单次查询的时间复杂度必然要小于On
当你发现单次询问无论如何也无法化简时间,这时需要改变思路了。
查询O1:前缀和。但由于此题的k和n不固定,该方法行不通
查询Ologn:树状数组。
由此引出正解——
我们可以先预处理,筛出3e6以内的质数,可以顺势找出3e6内的每个数的最小的质因数,再离线读入所有的n,对其进行从小到大的排序,利用树状数组可以Ologn求出最小质因数>=k的n的个数
最后注意要按询问顺序输出答案,我们可以给结构体多加一维统计输入序号,标记一下即可
code
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define rrep(i,a,b) for(int (i)=(a);(i)>=(b);(i)--) #define sc(x) scanf("%d",&(x)) #define scl(x) scanf("%lld",&(x)) #define pb push_back using namespace std; const int N = 3e6+10,mod = 1e9+7; int cnt,q; int p[300006],minp[N]; bool vis[N]; ll c[N]; struct node{ int num; int k; int id; bool operator < (const node &aa)const{ return num<aa.num; } }a[N]; int n=3e6; int ans[N]; int lowbit(int x){ return x&-x; } void add(int i,int d){ while(i<=n){ c[i]+=d; i+=lowbit(i); } } ll sum(int i){ ll res=0; while(i>0){ res+=c[i]; i-=lowbit(i); } return res; } void init(int n){ memset(vis,0,sizeof(vis)); cnt=0; for(int i=2;i<=n;i++){ if(!vis[i]) p[++cnt]=i; for(int j=1;j<=cnt&&i*p[j]<=n;j++){ vis[i*p[j]]=1; if(i%p[j]==0) break; } } rep(i,2,n){ if(vis[i]==0){ minp[i]=i; continue; } rep(j,1,cnt){ if(i%p[j]==0){ minp[i]=p[j]; break; } } } } int main(){ init(3e6); // sc(q); // rep(i,1,q) cout<<i<<" "<<minp[i]<<endl; sc(q); rep(i,1,q) sc(a[i].num),sc(a[i].k),a[i].id=i; sort(a+1,a+1+q); int num=1; rep(i,1,q){ if(a[i].num>num){ rep(j,num+1,a[i].num){ add(minp[j],1); } num=a[i].num; } if(a[i].k==1){ ans[a[i].id]=sum(n); } else ans[a[i].id]=sum(n)-sum(a[i].k-1); } rep(i,1,q){ printf("%d\n",ans[i]); } return 0; }

浙公网安备 33010602011771号