思路:
做法1:
离线暴力打表存起来,然后再询问。
做法2:
找到5e5之内的素数,两两相乘,再结合素数的立方,排个序,再询问。
一个有四个因子的整数要么是一个素数的立方,要么是两个不同素数的乘积。
做法3:
枚举每个数,对它的倍数进行计数。那么最后要求计数器为4的数字。
开始用第一种方法发做,一直TLE,感觉应该是最后判断因子数是4的时候超时了,后来看了下题解,选择第二种方法做。
证明:第一种情况很明显,就i是1和本身再加上两个质因子(任何一个合数都能表示成几个质因子的乘积)。第二种情况,即三个素数的乘积。因为相同的两个素数乘出来的数一定是只有三个因数的。(一个素因子加1和本身),也就是说它不能分解成除1和本身外的另外两个因数相乘,即可以看作乘出来的数是不可分的。则a可拆成(p*p) p a 1四个因子。
所以只要把范围内的所有素数两两相乘和素数的立方存起来,查询一遍就可以。
(这里有个一直模糊不清的概念,就是1000ms约等于10^8,开始以为是1e9,一直没找到TLE的地方)
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stdlib.h>
#include<iostream>
#include<map>
using namespace std;
const int maxn=5e5;
int n,a,b,prime[maxn],primesize,cnt,cnt2,num[maxn];
bool isprime[maxn];
vector<int> v;
map<int,int> m;
void getlist(int listsize){
memset(isprime,1,sizeof(isprime));
isprime[1]=false;
for(int i=2;i<=listsize;i++){
if(isprime[i])
prime[++primesize]=i;
for(int j=1;j<=primesize&&i*prime[j]<=listsize;j++){
isprime[i*prime[j]]=false;
if(i*prime[j]==0)
break;
}
}
}
void init(){
//cout<<primesize<<endl;
cnt=0;
memset(num,0,sizeof(num));
for(int i=1;i<=primesize;i++){
if(prime[i]>=maxn)
break;
for(int j=1;j<=primesize;j++){
if(i==j)
continue;
if(prime[i]*prime[j]>maxn)
break;
int ans=prime[i]*prime[j];
m[ans]=1;
}
}
for(int i=1;i<primesize;i++){
int ans=prime[i]*prime[i]*prime[i];
if(ans>maxn)
break;
m[ans]=1;
}
for(int i=1;i<=maxn;i++){
if(m[i]==1)
num[i]=num[i-1]+1;
else
num[i]=num[i-1];
}
}
int main(){
//freopen("out.txt", "w", stdout);
getlist(maxn);
init();
while(scanf("%d",&n)!=EOF){
while(n--){
cnt2=0;
scanf("%d%d",&a,&b);
//cout<<num[a]<<" "<<num[b]<<endl;
if(m[a]==1)
cnt2=num[b]-num[a]+1;
else
cnt2=num[b]-num[a];
//memset(num,0,sizeof(num));
/*for(int i=a;i<=b;i++){
if(m[i]==1)
cnt++;
}*/
printf("%d\n",cnt2);
}
}
return 0;
}
cf好像有一道类似的更简单一点。。有空给做了