=== ===
这里放传送门
=== ===
题解
这题记得某天学长出过胡策。。然后一脸懵逼:这玩意儿也能求?!
这题用到了一个非常常用非常重要的性质:对于long long级别的数字,它不同的质因子个数非常小,最多不会超过15个。因为最小的十来个质数乘起来就已经变得很大了。这就说明我们可以用爆搜来搞这个东西。对于这道题来说我们知道一个数的phi就是一坨质数和一坨(质数-1)的乘积,那么我们的任务就是把这些东西构造出来。
枚举所有的prime,如果这个prime合法的话那么prime-1一定是给定的x的约数。如果x不能整除prime-1的话就根本不用考虑这个质数了,否则就开始考虑把prime这个东西构造到答案里面去。首先除掉一个prime-1,答案乘以一个prime;接下来就一个一个地除掉prime,然后每次都进去搜一次直到不能除了为止,这相当于枚举了这个质因数的次数。
然而还有一个问题就是对于那些很大的质数没法筛出来,这就要利用每个数大于 x√ 的质因子只会有一个,并且这个质因子一定表现为prime-1的形式,那么每次就判断一下当前剩下的这个数+1是不是一个大质数,如果是大质数的话就记录答案并且不用继续往下搜了,因为再往下搜答案只会再变大。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 50000
using namespace std;
int x,prm[50010];
long long ans;
bool ext[50010];
void get_prime(){
for (int i=2;i<=N;i++){
if (ext[i]==false) prm[++prm[0]]=i;
for (int j=1;j<=prm[0];j++){
if ((long long)i*prm[j]>N) break;
ext[i*prm[j]]=true;
if (i%prm[j]==0) break;
}
}
}
bool is_prime(long long x){
for (int i=2;i<=floor(sqrt(x));i++)
if (x%i==0) return false;
return true;
}
void search(int last,long long res,long long sum){
if (res==1){ans=min(ans,sum);return;}
if (res>floor(sqrt(x))&&is_prime(res+1)){
ans=min(ans,sum*(long long)(res+1));
return;//直接退出,因为后面即使搜索也搜不到更优解了
}//注意必须当剩下的数大于sqrt(x)的时候才能算成大质数不然会错
for (int i=last+1;i<=prm[0];i++){
if (res%(prm[i]-1)==0){
int nwres=res/(prm[i]-1);
long long nwsum=sum*(long long)prm[i];
search(i,nwres,nwsum);
while (nwres%prm[i]==0){
nwres/=prm[i];
nwsum=nwsum*(long long)prm[i];
search(i,nwres,nwsum);//枚举质因数的次数,每次都要search
}
}
if (prm[i]>res) return;
}
}
int main()
{
freopen("phi.in","r",stdin);
freopen("phi.out","w",stdout);
get_prime();
ans=2147483647;++ans;
scanf("%d",&x);
search(0,x,1);
if (ans<=2147483647) printf("%d\n",ans);
else printf("-1\n");
return 0;
}
偏偏在最后出现的补充说明
常用的结论!常用的结论!常用的结论!
注意搜索的姿势!姿势写不对怎么改怎么T!