BZOJ3643-欧拉函数,搜索

给定一个数x,我们可以求出phi(x)

那给定一个数k(k<1e6),如何求出phi(x)=k的解呢

容易知道k为奇数时唯有k=1有解x=1,其余无解

 

假设n有素幂因子分解$n={p_1}^{a_1}{p_2}^{a_2}\cdots{p_k}^{a_k}$

因为$\phi(x)=\prod\limits_{j=1}^{k}{p_j}^{a_j-1}(p_j-1)$

所以$1<p_j<=k$且$(p_j-1)\mid k$

由此筛选出一些质数,同时记录它的最高幂次

然后用dfs遍历所有的情况,找到答案就将答案加入ans

 

以k=8为例,设$x=2^a3^b5^c$

对于2,有$(2-1)2^{a-1}\mid 8$,则a最大为4

对于3,有$(3-1)3^{b-1}\mid 8$,则b最大为1

对于5,有$(5-1)5^{c-1}\mid 8$,则c最大为1

再进行bfs搜索,得到可行解

a b c x
0 1 1 15
4 0 0 16
2 0 1 20
3 1 0 24
1 1 1 30

 

 

发现某些偶数是无解的,随着x的增大,phi(x)似乎越来越稀疏

 

代码

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=500000+5;
bool prime[N];
int p[N], tot=0;
//线筛
void initP(){
    for(int i=2;i<N;i++)prime[i]=true;
    for(int i=2;i<N;i++){
        if(prime[i])p[tot++]=i;
        for(int j=0;j<tot&&i*p[j]<N;j++){
            prime[i*p[j]]=false;
            if(i%p[j]==0)break;
        }
    }
}
//快速幂
LL qpow(LL a,LL b){
    LL ret=1;
    while(b){
        if(b&1)ret=(ret*a);
        a=(a*a);
        b>>=1;
    }
    return ret;
}
typedef pair<LL,LL> pr;
vector<LL> ans;
vector<pr>v;
int num[100];//保存状态
void add(){
    LL ret=1;
    for(int i=0;i<v.size();i++){
        if(num[i])ret*=qpow(v[i].first,num[i]);
    }
    ans.push_back(ret);
}
//now代表当前状态的欧拉函数值
void dfs(int cur,LL now,LL n){
    if(now==n&&cur==v.size())add();//找到答案
    if(now>n||cur==v.size())return;//剪枝与边界
    dfs(cur+1,now,n);       //cur位置上的数为0
    now*=v[cur].first-1;
    num[cur]=1;
    dfs(cur+1,now,n);       //cur位置上的数为1
    for(int i=1;i<v[cur].second;i++){
        now*=v[cur].first;
        num[cur]++;
        dfs(cur+1,now,n);   //cur位置上的数>1
    }
    num[cur]=0;               //回溯
}
bool isPrime(LL n){
    if(n<N)return prime[n];
    for(int i=0;i<tot;i++){
        if(n%p[i]==0)return false;
    }
    return true;
}
void solve(LL n){
    v.clear();
    ans.clear();
    memset(num,0,sizeof num);
    LL m,tmp,last=n;
    //分解质因数
    for(int i=0;i<tot&&p[i]<n;i++){
        if(n%(p[i]-1)==0){
            m=1;
            tmp=n/(p[i]-1);
            while(tmp%p[i]==0)m++,tmp/=p[i];
            //printf("%d - %d\n",p[i],m);
            v.push_back(pr(p[i],m));
        }
    }
    if(isPrime(n+1))v.push_back(pr(n+1,1));
    dfs(0,1,n);
    sort(ans.begin(),ans.end());   //排序
}
int main(){
    initP();
    LL n;
    while(~scanf("%lld",&n)){
        solve(n);
        if(ans.size()==0||ans[0]>(1LL<<31))printf("-1\n");
        else printf("%lld\n",ans[0]);
    }
    return 0;
}

  

  

  

  

posted @ 2017-10-25 01:50  水明  阅读(325)  评论(0编辑  收藏  举报