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; }