[BZOJ3643]Phi的反函数(数论+dfs)

=== ===

这里放传送门

=== ===

题解

这题记得某天学长出过胡策。。然后一脸懵逼:这玩意儿也能求?!

这题用到了一个非常常用非常重要的性质:对于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!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值