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

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


        -更新-

听说被hack了?

贴个最新代码吧 BZOJ4803真子集

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

using namespace std;
typedef long long LL;

const int N=100005;

int k,pri[N],pcnt,S,len,num[N>>4];
bool fl[N];
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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值