CodeForces - 83D:Numbers (数学&递归 - min25筛 )
pro:给定三个整数L,R,P求[L,R]区间的整数有多少个是以P为最小因子的。L,R,P<2e9;
sol:
一: 比较快的做法是,用函数的思想递归。
用solve(N,P)表示求1到N有多少数字多少个的最小因子是P;
1,首先P是合数,或者N<P;solve=0;
2,否则,如果P*P>=N;solve=1;
3,solve=N/P-solve(N/P,i); 2<=i<P
由于P主要分布在sqrt(N),而且N每次log级别减小,所以收缩得很快。具体的复杂度我证明不来,但是感觉过程和min25筛差不多。 (62ms
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; bool check(int P) { for(int i=2;i*i<=P;i++) if(P%i==0) return false; return true; } int solve(int N,int P) { if(!check(P)||N<P) return 0; if(N/P<P) return 1; int res=N/P; rep(i,2,P-1) res-=solve(N/P,i); return res; } int main() { int A,B,P; scanf("%d%d%d",&A,&B,&P); printf("%d\n",solve(B,P)-solve(A-1,P)); return 0; }
二:当时还不流行min25筛,否则这题大部分人都可以套板子了。我们知道min25的过程其实就是每次可以得到最小素因子为p的数的个数(或者之和),所以改一下板子即可(128ms
#include<bits/stdc++.h> using namespace std; #define ll int const int maxn=200010; ll Sqr,vis[maxn],pri[maxn],tot,m,id1[maxn],id2[maxn]; ll g[maxn],w[maxn]; //sp前i个素数之和。 void Sieve(int n) { tot=0; vis[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) pri[++tot]=i; for(int j=1;pri[j]<=n/i;j++){ vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } } bool check(int P) { for(int i=2;i*i<=P;i++) if(P%i==0) return false; return true; } ll solve(ll n,ll K) { if(n<K) return 0; if(!check(K)) return 0; if(K>n/K) return 1; Sqr=sqrt(n); Sieve(Sqr); ll res=0; m=0; for(ll i=1,j;i<=n;i=j+1){ j=n/(n/i); w[++m]=n/i; if(w[m]<=Sqr) id1[w[m]]=m; else id2[n/w[m]]=m; g[m]=w[m]-1; //1到n的素数个数,先设为n-1(1不考虑 } for(int j=1;j<=tot;j++){ for(int i=1;i<=m&&pri[j]<=w[i]/pri[j];i++){ int k=(w[i]/pri[j]<=Sqr)?id1[w[i]/pri[j]]:id2[n/(w[i]/pri[j])]; g[i]=g[i]-(g[k]-(j-1)); if(pri[j]==K&&i==1) res+=g[k]-(j-1); //K去筛,[1,N]时 } } return res+1;//加上素数自己 } int main() { int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\n",solve(r,k)-solve(l-1,k)); return 0; }
It is your time to fight!