http://oj.acm.zstu.edu.cn/JudgeOnline/problem.php?id=4274
4274: 约素
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 1890 Solved: 493
Description
判断一个正整数n的约数个数是否为p,其中p是素数。
Input
第一行给测试总数T(T <= 10000)。
接下来有T行,每行有两个数字n(1 <= n <= 1000000000)和p(2 < p <= 1000000000)。
Output
每组测试数据输出一行,如果n的约数个数是p,输出“YES”,否则输出“NO”。
Sample Input
5
64 7
911 233
1080 13
1024 11
20170211 1913
Sample Output
YES
NO
NO
YES
NO
题意很清楚, 无需多说什么。
我给出三种解法:
第一种:超时的。(数据组数太多)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
using namespace std;
typedef long long LL;
int n, p;
int main()
{
int t;
for (scanf("%d", &t); t; t--){
scanf("%d%d", &n, &p);
int cnt=1;
for (LL i=2; i*i<=n; i++){
if (n%i==0){//找到一个素因子
int num=0;
while (n%i==0) {
num++;
n/=i;
}
cnt*=(1+num);//算术基本定理的结论
}
}
if (n!=1){
cnt*=2;//必不可少
}
if (cnt!=p) printf("NO\n");
else printf("YES\n");
}
return 0;
}
第二种:(预处理, 时间复杂度是没有问题的, 但是还是超时了,因为程序有错)
首先给出一个错误的程序:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
using namespace std;
typedef long long LL;
int N, P;
const LL maxn=1e6;
bool prime[maxn];
LL p[maxn];
LL cn;
void cnt_prime()
{
cn=0;
memset(prime, 0, sizeof prime);
for (LL i=2; i<maxn; i++){
if (!prime[i]){
p[cn++]=i;if (cn==1)
for (LL j=i*i; j<=maxn; j+=i) {//这里出现了一个不那么显然的错误, 一不小心就gg了
prime[j]=true;
}
}
}
}
int main()
{
int t;
cnt_prime();cout<<p[0]<<endl;
for (scanf("%d", &t); t; t--){
scanf("%d%d", &N, &P);
LL cnt=1;
for (LL i=0; i<cn && p[i]*p[i]<=N; i++){
if (N%p[i]==0){
LL num=0;
while (N%p[i]==0) {
num++;
N=N/p[i];
}
cnt*=(1+num);
}
}
if (N>1){
cnt*=2;
}
if (cnt!=P) printf("NO\n");
else printf("YES\n");
}
return 0;
}
上面这个程序会导致超时, 经过多次测试, 我发现了p[0]==1!!!! 是的, 其它的素数都对了, 但第一个素数应该是2。想不到原因啊!(临时把p[0]强制赋值为2当然是可以AC了, 但是为什么会出现p[0]==1呢?)很无奈, 问了别人, 也是看不出来。好吧,最终还是硬着头皮一句一句分析找原因, 原来是“j<=maxn”这里考虑不周:当j==maxn时, prime[maxn]=true; 也就是p[0]=true=1! Orz…因为prime的下标只能去到maxn-1, 所以prime[maxn]对应的地址就是p[0]的地址。这种问题, 我以前给别人看程序的时候出现过一次, 也是花了很多时间才找出来的。 没想到这次再次出现了还是不能一眼察觉。。。还是不够细心严谨啊, 这就是教训!所以把“j<=maxn” 中的等号去掉即可AC。
第三种方法:(应该是最高效的了吧)
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
const double eps=1e-8;
typedef long long LL;
bool is_prime(int x)
{
if (x<2) return false;
if (x==2 || x==3) return true;
for (LL i=2; i*i<=x; i++){
if (x%i==0) return false;
}
return true;
}
int main()
{
int t;
for (scanf("%d", &t); t; t--){
int n, p;
scanf("%d%d", &n, &p);
int x=pow(n+0.5, 1.0/(p-1));//p是素数,=> n只有一个素因子x,=> p=(1+p-1); => x^(p-1)==n
if (fabs(pow(x, p-1)-n)<eps && is_prime(x)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
第三个程序成立的必要条件是p是素数, 这个是题目给的。 若n=p0^e0 + p1^e1 + … + pi^ei;
则p=(1+e0)* (1+e1) * … *(1+ei) => p只有一个素因子, 素因子只能是整数, 于是得以上程序。
完。