2019 ICPC南昌网络赛 A 题 Enju With math problem

题目链接

题意

给100个数,找到在欧拉函数中连续出现的位置,找不到输出 -1

题解

在素数密度较大的情况下,想到最大的数 x x x , x + 1 x+1 x+1 应该是一个素数
所以判断一下是否为素数,检查100个是否都满足即可

但是,打表发现,会出现相邻素数的距离大于100,那么上面的方法就不适用了
考虑一个性质
n , m n,m n,m 互质时 φ ( n ∗ m ) = φ ( n ) ∗ φ ( m ) = ( n − 1 ) ∗ ( m − 1 ) \varphi (n*m)=\varphi (n)*\varphi(m)=(n-1)*(m-1) φ(nm)=φ(n)φ(m)=(n1)(m1)
最大的数,应该能分解为两个素数的欧拉函数的乘积
证明的话,我也不明白。。。
然后暴力分解,判断即可

由于求欧拉函数的次数较多,所以要用 n 1 4 n^{\frac{1}{4}} n41 的求法

其实,用 M i l l e r _ R a b i n Miller\_Rabin Miller_Rabin 判断 i n t int int 范围内的素数不是最优的选项,实际中,要检测10多个才能保证正确,换用其他的素数检测效率会跟更高,而且不是基于概率的。

代码
#include<bits/stdc++.h>
#define N 1000010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi 3.141592653589793
// #define mod 1000000007
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
LL ans;
map<LL,bool>have;
inline LL mul(LL a,LL b,LL p){
    return a*b%p;
}
inline LL pow(LL a,LL n,LL p){
    LL res=1;
    while(n){
        if(n&1)res=mul(res,a,p);
        a=mul(a,a,p);n>>=1;
    }
    return res;
}
bool check(LL a,LL m,LL n,LL cnt){
    LL x=pow(a,m,n),y=x;
    for(int i=1;i<=cnt;i++){
        x=mul(x,x,n);
        if(x==1&&y!=1&&y!=n-1)return 1;
        y=x;
    }
    return x!=1;
}
bool Miller_Rabin(LL n){
    if(n==2)return 1;
    if(n&1==0||n<=1)return 0;
    LL m=n-1,cnt=0;
    while(m&1==0)cnt++,m>>=1;
    for(int i=1;i<=15;i++){
        if(check(rand()%(n-1)+1,m,n,cnt))
        return 0;
    }
    return 1;
}
LL rho(LL a,LL n,LL c){
    LL i=1,k=2,x=a,y=x,d;
    while(1){
        x=(mul(x,x,n)+c)%n;
        d=x>y?__gcd(x-y,n):__gcd(y-x,n);
        if(d>1)return d;
        if(x==y)return n;
        if(i==k)y=x,k<<=1;
        i++;
    }
}
// std::vector<int> v;
void Pollard_rho(LL n){
    if(n==1)return;
    if(Miller_Rabin(n)){
        if(!have[n])ans=ans/n*(n-1);
        have[n]=1;
    	// v.pb(n);
        return;
    }
    LL p=n;
    while(p==n)p=rho(rand()%(n-1)+1,n,rand()%(n-1)+1);
    Pollard_rho(p),Pollard_rho(n/p);
}

int phi[N],prime[N],num;
bool flag[N];
void pre(int n){
    phi[1]=1;
    for (int i=2;i<=n;i++){
        if (!flag[i]){
            prime[++num]=i;
            phi[i]=i-1;
        }
        for (int j=1;j<=num&&prime[j]*i<=n;j++){ 
            flag[i*prime[j]]=1;
            if (i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }else phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}

LL eular(LL n){
	if (n<N) return phi[n];
	have.clear();
	ans=n;
	Pollard_rho(n);
	return ans;
}

int a[150];

inline bool check(int ans){
	for(int i=0;i<100;i++)
		if (eular(i+ans)==a[i]) continue;
		else return false;
	return true;
}

bool solve(int pos){
	if (Miller_Rabin(a[pos]+1)){
		int ans=a[pos]+1-pos;
		if (check(ans)) {
			printf("YES\n%d\n",ans);
			return 1;
		}
	}
	for(int i=1;i<=num;i++){
		if (a[pos]%(prime[i]-1)==0&&Miller_Rabin(a[pos]/(prime[i]-1)+1)){
			LL ans=1ll*prime[i]*(a[pos]/(prime[i]-1)+1)-pos;
			if (ans>150000000) continue;
			if (check(ans)){
				printf("YES\n%lld\n",ans);
				return 1;
			}
			if (1ll*prime[i]*prime[i]>a[pos]*2) break;
		}
	}
	puts("NO");
	return 0;
}

int main()
{
    srand(time(0));
    pre(N-1);
    int T; sc(T);
    while(T--){
    	for(int i=0;i<100;i++) sc(a[i]);
    	int k=max_element(a,a+100)-a;
    	solve(k);
    		
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值