和Leo一起做爱数学的好孩子之 CERC2017 Faulty Factory 阶乘谜题

版权声明:LeoJAM Presents https://blog.csdn.net/fcb_x/article/details/82707479

给定n,质数p和一个目标余数r

你需要改变n的阶乘中的一个数字,使得其积模p余r。

感觉不能融会贯通

我以为是个EXGCD的神仙题

首先n看似很大,但是明显n并不是完全有用

2*p<=n时若余数不为0则无解

对于p<n有:

(\prod_{i}^{n}i)/p\equiv r (mod=p)这个明显是一个不定方程,算出最小解再判断和p的大小关系就好了

但是n<p怎么办?

由于这个不超过1e7我认为直接枚举变的暴力EXGCD

O(log_{\frac{1+\sqrt{5}}{2}}N)但很明显这是不能A掉的(虽然开了3s)

这个时候思考BSGS(北上广深新一线算法)

我们有一个在同余方程移位的操作

(\prod_{i-1}^{n}i)/j*x\equiv r(mod=p)等价于x\equiv Inv(\prod_{i=1}^{n}i)*j*r (mod=p)

如果线性预处理逆元这是O1查询的

那么就A了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
LL n,p,r;
LL Check(LL Id,LL sum,LL mod){
	LL ret=1;
	for(int i=1;i<=n;++i){
		if(i==Id)ret=ret*sum%mod;
		else ret=ret*i%mod;
	}
	return ret;
}
void Solve50(){
	for(int i=2;i<=n;++i){
		for(int j=1;j<i;++j){
			if(Check(i,j,p)==r){
				cout<<i<<" "<<j;
				return;
			}
		}
	}
	cout<<-1<<" "<<-1;
}
void EXGCD(LL &x,LL &y,LL A,LL B){
	if(B==0){
		x=1;
		y=0;
		return;
	}
	else{
		EXGCD(x,y,B,A%B);
		LL tmp=x;
		x=y;
		y=tmp-(A/B)*y;
	}
}
const int N=1e7+10;
LL F[N];
void Solve100(){
    if (n == p) {
        if (r == 0) {
            if (n == 2) cout << -1 << " " << -1 << endl;
            else cout << 2 << " " << 1 << endl;
            return;
        }
        else {
            for (LL i=1; i<p; i++)  if (i*(p-1)%p==r) {
                cout << p << " " << i << endl;
                return;
            }
            cout << -1 << " " << -1 << endl;
            return;
        }
    }
	if(n>=2*p){
		if(!r)cout<<p+1<<" "<<1;
		else cout<<-1<<" "<<-1;
		return;
	}
	F[0]=1;

	if(p>n){
		LL x,y;
		if(!r){
			cout<<-1<<" "<<-1<<'\n';
			return ;
		}
		for(int i=1;i<=n;++i){
			F[i]=F[i-1]*i%p;
		}	
		EXGCD(x,y,F[n],p);
		LL Inv=(x+p)%p;
		for(int i=2;i<=n;++i){
			LL tmp=Inv*i%p*r%p;
			if(tmp<i&&tmp){
				cout<<i<<" "<<tmp<<'\n';
				return;
			}
		}	
		cout<<-1<<" "<<-1;
	}
	else{
		if(!r){
			cout<<p+1<<" "<<1<<'\n';
			return;
		}
		LL sum=1;
		for(int i=1;i<=n;++i){
			if(i==p)continue;
			sum=sum*i%p;
		}
		LL x,y;
		EXGCD(x,y,sum,p);
		x=(x+p)%p;
		if(x*r%p<=p){
			cout<<p<<" "<<x*r%p;
		}
		else cout<<-1<<" "<<-1;
	}
}
int main(){
//	freopen("f.in","r",stdin);
//	freopen("f.out","w",stdout);
	cin>>n>>p>>r;
	if(n<=500){
		Solve50();
	}
	else{
		Solve100();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/fcb_x/article/details/82707479