0628Zn

比较巧妙的题

前置知识点
欧拉函数
中国剩余定理
题目描述
求在\([1,x]\)中有多少\(n\),满足:

\(n\cdot a^n≡b\pmod p\)

输入格式
一行四个数分别表述\(a,b,p,x\)

输出格式
一行一个数表述答案

数据范围
\(1≤x≤10^{12},1≤a,b,p≤10^9,保证p与a互质\)

题面基础,数据范围就不基础(不是)
显然我们需要慢慢思考
最简单的想法肯定是暴力找\([1,x]\)的所有数进行判断,但这显然会超
聪明的你肯定想到,既然\(a,p\)互质,那么\(a^{\varphi(p)}\equiv1\pmod p\),所以\(n\cdot a^n\)在模\(p\)意义下以\({lcm}(p,\varphi(p))\)为一个周期循环,那么我们只需要遍历\({lcm}(p,\varphi(p))\)以内的\(x\)就好了。
然而这还是很大的范围啊…
我们又注意到,\(a^n与n\)的周期都要短的多,而且之后的值我们确实都分别算过了
于是我们可以遍历\(i\in [1,\varphi(p)]\)的所有数,假如此时存在\(n\)满足条件,则有
\(\left\{\begin{matrix} n\equiv i\pmod{\varphi(p)}\\ n\equiv a^{-i}b\pmod p \end{matrix}\right.\)
可以使用EXCRT求解该方程组,最后统计答案即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
long long a,b,x,p,t,phi,inv,lcm,ans,mi,k1,_k2,gcd;
queue <long long> q;
void getphi(long long n,long long &a)
{
	a=1;
	for(long long i=2;i*i<=n;i++)
	{
		if(n%i)
			continue;
		a*=i-1,n/=i;
		while(n%i==0)
			n/=i,a*=i;
	}
	if(n>1)
		a*=n-1;
}
long long exgcd(long long a,long long &x,long long b,long long &y)
{
	if(!b)
	{
		x=1,y=0;
		return a;
	}
	long long d=exgcd(b,y,a%b,x);
	y=y-a/b*x;
	return d;
}
//long long mul(long long a,long long b,long long p)
//{
//	long long s=0,f=a<0?-1:1;
//	a*=f;
//	for(;b;b>>=1,a=a<<1,a-=a>=p?p:0)
//		if(b&1)
//			s=s+a,s-=s>=p?p:0;
//	return s*f;
//}
int main()
{
	scanf("%lld %lld %lld %lld",&a,&b,&p,&x);
	a%=p,t=b%=p;
	getphi(p,phi);
	exgcd(a,inv,p,k1);
	gcd=exgcd(phi,k1,p,_k2);
	lcm=p/gcd*phi;
	for(long long i=1;i<=x&&i<=phi;++i)
	{
		t=t*inv%p;
		if((t-i)%gcd)
			continue;
		long long an;
		an=(t-i)/gcd*k1%lcm*phi%lcm+i;
		if(an<0)
			an+=lcm;
		if(an>=lcm)
			an-=lcm;
		if(an<=x)
			ans+=(x-an)/lcm+1;
	}
	printf("%lld\n",ans);
	return 0;
}

posted on 2025-08-29 08:10  琬安与璃茗  阅读(10)  评论(0)    收藏  举报