您的位置:首页 > 其它

[BZOJ3643] phi的反函数 - 欧拉函数 - dfs

2016-05-16 20:51 465 查看

3643: Phi的反函数

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 118  Solved: 83

[Submit][Status][Discuss]

Description



Input

Output

Sample Input

4

Sample Output

5

HINT

Source

By Zky

        -更新-

听说被hack了?

贴个最新代码吧 BZOJ4803真子集

#include"bits/stdc++.h"
#define last last_

using namespace std;
typedef long long LL;

const int N=100005;

int k,pri
,pcnt,S,len,num[N>>4];
bool fl
;
set<LL> s;
set<LL>::iterator it;

void push(LL x){
s.insert(x);
if(s.size()<=k)return;
s.erase(--(it=s.end()));
}

bool go(LL x){
if(s.size()<k)return true;
return x<*(--(it=s.end()));
}

const int jp[]={2,3,5,7,11,13,17};

LL mult(LL x,LL y,LL z){
return ((x*y-(LL)((long double)x*y/z+0.5)*z)%z+z)%z;
}

LL power(LL a,LL t,LL P){
LL r=1;
while(t){
if(t&1)r=mult(r,a,P);
a=mult(a,a,P);t>>=1;
}
return r;
}

bool Miller_Rabin(LL n){
if(n==2||n==3||n==5||n==7||n==13||n==17||n==19||n==23||n==29||n==31)return true;
if(n%2&&n%3&&n%5&&n%7&&n%11&&n%13&&n%17&&n%19&&n%23&&n%29&&n%31){
LL r=n-1;
int t=0;
while(!(r&1))r>>=1,t++;
for(int i=0;i<7;i++){
LL x=power(jp[i],r,n),y;
for(int j=0;j<t;j++){
y=mult(x,x,n);
if(y==1&&x!=1&&x!=n-1)return false;
x=y;
}
if(x!=1)return false;
}
return true;
}
return false;
}

void dfs(int p,LL n,LL m){
if(n==1){
if(p)push(m*2);
push(m);
return;
}
if((double)(n+1)*m>1E17)return;
if(!go((n+1)*m))return;
if(p==0){
if(n>=S&&Miller_Rabin(n+1))push(m*(n+1));
if(n==1)push(m);
return;
}
dfs(p-1,n,m);
if(n%(num[p]-1)==0){
n=n/(num[p]-1)*num[p];
while(n%num[p]==0){
n/=num[p],m*=num[p];
dfs(p-1,n,m);
}
}
}

LL n;

int main(){
cin>>n; k=1;
S=3*(int)sqrt(n+.1);
for(int i=2;i<=S;i++){
if(!fl[i]){
pri[++pcnt]=i;
if(n%(i-1)==0)num[++len]=i;
}
for(int j=1;i*pri[j]<=S;j++){
fl[i*pri[j]]=1;
if(i%pri[j]==0)break;
}
}
dfs(len,n,1);
if(s.empty()||*s.begin()>0x7FFFFFFF) puts("-1");
else printf("%lld\n",*s.begin());
return 0;
}

-分割线-

这里应该就不需要我介绍欧拉函数了吧 = = 。

        然后这题我跑得太快进首页了 = =。

        首先我们设答案ans = p1^a1 * p2^a2 * …… * pm^am 其中pi是素数且对于任意i,j有pi≠pj

        那么显然n = p1^(a1-1) * p2^(a2-1) * …… * pm^(am-1) * (p1-1) * (p2-1) * …… * (pm-1)

        那么我们就可以DFS了。暴力凑(p1-1) * (p2-1) * …… * (pm-1),然后将n与其相除后验证是否是这些数的倍数即可。

        所以我们预处理出1~sqrt(n)的所有素数表,因为我们只会用到这些素数。如果有pk大于这个范围,显然不存在t≠k且pt也不在范围。

        所以对这种情况进行处理一下,只求出这样的数查看是否正确即可。

        这样程序的DFS就分成了三个流程: 立即结尾,乘上一个大素数后结尾,和继续扩展三种

        然而这个DFS催人泪下……

#include "stdio.h"
#include "iostream"
#include "math.h"
using namespace std;
typedef long long ll;

int ask[15],prime[10005],n;
bool flag[100005]; int cnt;
unsigned int ans=1+(1<<31);
unsigned int noans=ans;

void makeprime(){
int i,j; flag[1]=false;
for (i=2;i*i<=n;i++){
if(!flag[i]) prime[++cnt]=i;
for (j=0;prime[j]*prime[j]*i*(ll)i<=n
&&j<=cnt;flag[prime[j]*i]=true,j++);
}
}

void dfs(int pos, //the position now
int rem, //the number remain
ll mul, //the prime multed
int last //last prime collected
){
int i,tot,j;
//end
if(pos>1){ //it can be ended
for (i=1,tot=rem;i<pos;i++)
while(tot%ask[i]==0) tot/=ask[i];
if(tot==1&&rem*mul<ans) ans=rem*mul;
}
//can't work any longer
if(rem<prime[last+1]||last>cnt) return ;
//huge prime
for (i=1,tot=rem;i<pos;i++)
while(tot%ask[i]==0) tot/=ask[i];
for (j=2,++tot;j*j<=tot;j++)
if(tot%j==0) break;
if(j*j>tot&&rem/(tot-1)*mul*tot<ans)
ans=rem/(tot-1)*mul*tot;
//not last prime
for (i=last+1;(prime[i]-1)*(prime[i]-1)<=rem&&i<=cnt;i++){
if (rem%(prime[i]-1)==0){
ask[pos]=prime[i];
dfs(pos+1,rem/(prime[i]-1),
mul*prime[i],i);
}
}
}

int main(){
//freopen("phi.in","r",stdin);
//freopen("phi.out","w",stdout);
cin>>n; makeprime(); dfs(1,n,1,0);
if(ans==noans) puts("-1");
else cout<<ans<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: