B光滑数
Question
问题描述
B为一个正整数,如果一个自然数N的因子分解式中没有大于B的因子,我们就称N是一个B光滑数。请你编一个程序,求出某个区间中所有的B光滑数的个数。
输入格式
输入仅有一行,包含三个用空格隔开的整数n,m,b,其中1<=n<=2000000000,1<=m<=100000000,1<=b<=1000000。
输出格式
输出仅一行。一个整数,表示区间[n,n+m]之间的B光滑数的个数。
样例输入
30 10 5
样例输出
4
Analysis
B光滑数其实是一个密码学的定义。
这题递推式非常显然。
首先,“大于B的因子”,显然就是质因子分解。那么我们可以找出小于b的所有质数。
然后状态显然是:
这个式子的意思是,在[x,y]内找因子小于第p个素数的数的个数。
然后递推式显然是:
显然是加法原理,这个状态的ans是由这两个状态组合出来的,其中这两个状态没有交集,不会重复。并且不会遗漏。
边界直接就可以出来了。不妨多写一些。
如果x>y,显然return 0;
如果y<=P[p],就是说区间内最大的数也比第p个素数小,显然每个数都成立,直接return y-x+1.这个边界似乎并不是一定要的,只不过为了减少计算而已。
如果p=1,
就是p的边界了,这时候质数是2,直接用log计算是2的次方的个数即可。
Ok,就是这么简单了。
Code A
#include<bits/stdc++.h>
using namespace std;
int P[2000001];
int maxx;
int prim(int b){
P[1]=2;
int p=1;
int mm=0;
bool flag;
for (int i=3;i<=b;i+=2){
flag=true;
mm=sqrt(p)+1;
for (int j=1;j<=mm and P[j];++j)
if (!(i%P[j])){
flag=false;
break;
}
if (flag)
++p,
P[p]=i;
}
maxx=p;
return p;
}
int dfs(int p,int x,int y){
if (x>y)
return 0;
if (y<=P[p])
return y-x+1;
if (x==y){
for (int i=p;i<=maxx;++i)
if (!(x%P[i]))
return 0;
return 1;
}
if (p==1)
return trunc((log(y)/log(2))-trunc(log(x)/log(2)));
int ans=0;
ans=dfs(p-1,x,y)+dfs(p,(x-1)/P[p]+1,y/P[p]);
return ans;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
int n,m,b;
cin>>n>>m>>b;
int p;
p=prim(b);
int ans=0;
ans=dfs(p,n,n+m);
cout<<ans<<endl;
return 0;
}
嗯很好,25分,发生了什么?
很简单,log的范围判断问题,这个直接算是不对的,因为无法判断两个数之间有几个整数。
于是改一改,改成直接暴力计算个数吧.
Code B 80
#include<bits/stdc++.h>
using namespace std;
int P[1000001];
int maxx;
int prim(int b){
P[1]=2;
int p=1;
int mm=0;
bool flag;
for (int i=3;i<=b;i+=2){
flag=true;
mm=sqrt(p)+1;
for (int j=1;j<=mm and P[j];++j)
if (!(i%P[j])){
flag=false;
break;
}
if (flag)
++p,
P[p]=i;
}
maxx=p;
return p;
}
int dfs(int p,int x,int y){
if (x>y)
return 0;
if (y<=P[p])
return y-x+1;
int ans=0;
if (p==1){//NB
long long temp=1;
for (;;){
if (temp>=x and temp<=y)
++ans;
if (temp>y)
break;
temp*=2;
}
return ans;
}
if (x==y){
for (int i=p+1;i<=maxx;++i)
if (!(x%P[i]))
return 0;
return 1;
}
ans=dfs(p-1,x,y)+dfs(p,ceil((x-1)/P[p])+1,ceil(y/P[p]));
return ans;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
int n,m,b;
cin>>n>>m>>b;
if (b==1){
if (n>1){
cout<<0<<endl;
return 0;
}
else{
cout<<1<<endl;
return 0;
}
}
int p;
p=prim(b);
int ans=0;
ans=dfs(p,n,n+m);
cout<<ans<<endl;
return 0;
}
80,又发生了什么?
这就百思不得其解了。
先打个枚举的吧。
Code C 90
#include<bits/stdc++.h>
using namespace std;
int P[1000001];
int maxx;
int prim(int b){
P[1]=2;
int p=1;
int mm=0;
bool flag;
for (int i=3;i<=b;i+=2){
flag=true;
mm=sqrt(p)+1;
for (int j=1;j<=mm and P[j];++j)
if (!(i%P[j])){
flag=false;
break;
}
if (flag)
++p,
P[p]=i;
}
maxx=p;
return p;
}
bool check(int k){
int p=1;
while (p<=maxx){
while (!(k%P[p]))
k/=P[p];
++p;
if (k==1)
return true;
}
if (k==1)
return true;
return false;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
int n,m,b;
cin>>n>>m>>b;
int up=n+m;
int p=prim(b);
int ans=0;
if (b==1 and n==1){
cout<<1<<endl;
return 0;
}
if (b==1){
cout<<0<<endl;
return 0;
}
for (int i=n;i<=up;++i){
if (check(i))
++ans;
}
cout<<ans<<endl;
return 0;
}
有毒,能拿到90分。而且和之前的80分程序对拍上万次都找不到错误。
那么怎么递推递归呢?
按照题目意思,可以找出每个数的最大质因子。
然后怎么快速找出最大质因子呢?
老师说,应该用线性筛法,可以直接求出来。
然后当b小的时候,可以直接for循环找ans,这样比递归快。
Code C AC
#include<bits/stdc++.h>
using namespace std;
int vh[2000001],P[2000001];
long long n,m,b,maxx,cnt,js=-1;
void prime(){//线性筛法模板+求最大质因子
for (long long i=0;i<=1000000;++i)
vh[i]=i;
for (long long i=2;i<=1000000;++i){
if (vh[i]==i){
P[cnt++]=i;
if (i<=b)
js=cnt-1;
}
for(long long j=0;j<cnt and (longlong)i*P[j]<=1000000;++j){
vh[i*P[j]]=max(vh[i],P[j]);//记录最大质因子
if (!(i%P[j]))
break;
}
}
}
long long cal(long long fir,long long sec){
long long p=1,ans=0;
while (p<=sec){
if (p>=fir)
++ans;//为了避免神奇的小数的特判,只能这么打
p*=(long long)2;
}
return ans;
}
long long dfs(long long p,long long x,long long y){
if (x>y)
return 0;
if (p==0)
return cal(x,y);
long long ans=0;
if (y<=100000){
for (long long i=x;i<=y;++i)
if (vh[i]<=P[p])
++ans;
return ans;
}
ans=dfs(p-1,x,y)+dfs(p,(x-1)/P[p]+1,y/P[p]);
return ans;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
cin>>n>>m>>b;
if (b==1){
if (n>1){
cout<<0<<endl;
return 0;
}
else{
cout<<1<<endl;
return 0;
}
}
prime();
long long ans=0;
ans=dfs(js,n,n+m);
cout<<ans<<endl;
return 0;
}
Conclusion
这题恶心,递推式很快就出来了,但是这个边界啊实在是。。。
小数问题要注意,用小数的地方注意特判,不特判的话就枚举吧。