BZOJ1319 Sgu261Discrete Roots

Description

link

求满足 \(x^k\equiv a \ (mod\ p)\) 的所有的 \(x\)

\(x\in[0,p-1]\)

保证 \(p\) 为质数

Solution

先把 \(a=0\) 的判掉

我们发现一般的 \(bsgs\) 可以求指数,但是不能求底数

那么我们转化一发:

原根科技!!!

原根相关的一篇让我大概懂了的博客

求原根啥的 \(51nod\) 上面有题做

我们发现 \(p\) 是个素数,就会有原根

我们就可以把右边表示成

\[g^{id_a} \]

左边也就成了:

\[(g^{id_x})^k \]

所以推推式子:

这个\(id_x\times k\equiv id_a \ (mod\ \varphi(p))\)

理解:指数上有欧拉定理……

\(id_a\) 是可以 \(bsgs\) 求的,\(k\) 是个给的

然后就上 \(exgcd\)\(id_x\) 的解和通解求一求

最后输出就 \(OK\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	inline int ksm(int x,int y,int p)
	{
		int res=1; for(;y;y>>=1,(x*=x)%=p) if(y&1) (res*=x)%=p; 
		return res;
	}
	inline void exgcd(int a,int b,int &x,int &y)
	{
		if(!b) return x=1,y=0,void();
		exgcd(b,a%b,x,y); int t=x; x=y; 
		y=t-(a/b)*y;
		return ;
	}
	const int N=2e5+10;
	int d[N],cnt,p,k,a;
	inline bool check(int x)
	{
		for(int i=1;i<=cnt;++i) if(ksm(x,(p-1)/d[i],p)==1) return 0;
		return 1;
	}
	inline int find(int p)
	{
		int t=p-1; 
		for(int i=2;i*i<=t;++i) 
		{
			if(t%i!=0) continue;
			d[++cnt]=i; while(t%i==0) t/=i;
		} if(t!=1) d[++cnt]=t;
		int g=1; while(g<p) if(check(g)) return g; else g++;
		return -1;
	}
	map<int,int> mp;
	inline int bsgs(int y,int z,int p)
	{
		int s=ceil(sqrt(p)),t=z%p; mp[t]=0;
		for(int i=1;i<=s;++i) (t*=y)%=p,mp[t]=i;
		int base=ksm(y,s,p),now=1;
		for(int i=1;i<=s;++i) 
		{
			(now*=base)%=p; 
			if(!mp.count(now)) continue;
			int t=i*s-mp[now]; 
			return (t%p+p)%p;
		}
	}
	int num,out[N];
	signed main()
	{
		p=read(); k=read(); a=read();
		if(!a) return puts("1\n0"),0; 
		int g=find(p),ans=bsgs(g,a,p);
		int gcd=__gcd(k,p-1);
		if(ans%gcd) return puts("0"),0;
		int A=k/gcd,B=(p-1)/gcd,x,y;
		exgcd(A,B,x,y);
		(x+=B)%=B; x=(x*(ans/gcd)+B)%B;
		while(x<p-1)
		{
			out[++num]=ksm(g,x,p);
			x+=B;
		} sort(out+1,out+num+1);
		printf("%lld\n",num);
		for(int i=1;i<=num;++i) printf("%lld\n",out[i]);
		return 0;
	}
}
signed main(){return yspm::main();}

Review

如果出现了底数不好求的情况

我们用原根把底数的计算转到质数上再进行其他操作

猜你喜欢

转载自www.cnblogs.com/yspm/p/12901853.html