今天做的这道题呢,总算是有两道送分题了

虽然很简单却因为空间开小了丢了10分

这道题是一道群论题,除此之外呢还有数论的一些稀有知识

比如miller-rabin判断质数,以及pollard-rho求质因数

都是一些很好用的小技巧,只要掌握了这些东西写起来应该是不难的

群论考察的知识是burnside引理,有一些小小的思考在里面

按理来说这道题原本群论的那部分知识应该只是模版题而已

但是我却没有想到,还是见识的太少了

对于pollard-rho好像需要先把2作为质因数的情况给筛掉,否则会一直卡住

我采用的是floyed判圈法,x跳一步,y跳两步,如果相等则直接break

miller-rabin因为追求复杂度只找了5次

 

以下是dalao的证明:

对于要判断的数n

1.先判断是不是2,是的话就返回true。

2.判断是不是小于2的,或合数,是的话就返回false。

3.令n-1=u*2^t,求出u,t,其中u是奇数。

4.随机取一个a,且1<a<n

/*根据费马小定理,如果a^(n-1)≡1(mod p)那么n就极有可能是素数,如果等式不成立,那肯定不是素数了

因为n-1=u*2^t,所以a^(n-1)=a^(u*2^t)=(a^u)^(2^t)。*/

5.所以我们令x=(a^u)%n

6.然后是t次循环,每次循环都让y=(x*x)%n,x=y,这样t次循环之后x=a^(u*2^t)=a^(n-1)了

7.因为循环的时候y=(x*x)%n,且x肯定是小于n的,正好可以用二次探测定理,

如果(x^2)%n==1,也就是y等于1的时候,假如n是素数,那么x==1||x==n-1,如果x!=1&&x!=n-1,那么n肯定不是素数了,返回false。

8.运行到这里的时候x=a^(n-1),根据费马小定理,x!=1的话,肯定不是素数了,返回false

9.因为Miller-Rabin得到的结果的正确率为 75%,所以要多次循环步骤4~8来提高正确率

10.循环多次之后还没返回,那么n肯定是素数了,返回true

 

以及pollard-rho的实现方式,枚举的是a-b,据说运用了生日悖论

可以去看看DOGGU大佬的博客

无比的玄妙,我反正看的一愣一愣的

然后就贴代码了

#include<bits/stdc++.h>
using namespace std;
long long que[1000005],n,k,another[1000005],powe[1000005];
int tot,t,all,num[1000005],ass;
const int mod=998244353;
long long moc(long long x,long long mod)
{
	if(x<0) return x+mod;
	if(x>=mod) return x-mod;
	return x;
}
long long gcd(long long x,long long y)
{
	if(!y) return x;
	return gcd(y,x%y);
}
long long calc(long long x,long long y,long long mod)
{
	long long ans=0;
	while(y)
	{
		if(y&1) ans=moc(ans+x,mod);
		y>>=1;
		x=moc(x+x,mod);
	}
	return ans;
}
long long fast(long long x,long long y,long long mod)
{
	long long ans=1;
	while(y)
	{
		if(y&1) ans=calc(ans,x,mod);
		y>>=1;
		x=calc(x,x,mod);
	}
	return ans;
}
int matrix(long long x)
{
	int res=fast(k-1,x,mod);
	if(x&1)res-=k-1;
	else res+=k-1;
	return moc(res%mod+mod,mod);
}
long long pollard_rho(long long n,long long c)
{
	long long x=rand()%n,y=x,p=1;
	while(p==1)
	x=(calc(x,x,n)+c)%n,
	y=(calc(y,y,n)+c)%n,
	y=(calc(y,y,n)+c)%n,
	p=gcd(n,abs(x-y));
	return p;
}
bool miller_rabin(long long n)
{
	long long times,u;
	if(n<=1) return false;
	if(n==2) return true;
	if(!(n&1)) return false;
	for(times=0,u=n-1;!(u&1);u>>=1,times++);
	for(int i=1;i<=5;i++)
	{
		int a=rand()%(n-2)+2;
		long long x=fast(a,u,n);
		for(int j=1;j<=times;j++)
		{
			long long y=calc(x,x,n);
			if(y==1&&x!=1&&x!=n-1) return false;
			x=y;
		}
		if(x!=1) return false;
	}
	return true;
}
void find(long long n)
{
	if(n==1) return;
	if(!(n&1))
	{
		que[++tot]=2,find(n>>1);
		return;
	} 
	if(miller_rabin(n))
	{
		que[++tot]=n;
		return;
	}
	long long p=n;
	while(p==n) p=pollard_rho(p,rand()%(n-1)+1);
	find(p);
	find(n/p);
}
void dfs(int now,long long numm,long long phi)
{
	long long temp=1;
	for(int i=now+1;i<=all;i++)
	{
		long long ha=1;
		for(int j=1;j<=num[i];j++)
		{
			ha*=another[i];
			if(j!=num[i])
			dfs(i,numm*ha,phi*temp*(powe[i]/ha)/another[i]*(another[i]-1));
			else dfs(i,numm*powe[i],phi*temp);
		}
		temp=temp*(another[i]-1)%mod;
		for(int j=2;j<=num[i];j++)
		temp=temp*another[i]%mod;
	}
	ass=moc(ass+phi*temp%mod*matrix(numm)%mod,mod);
}
int main()
{
	freopen("necklace.in","r",stdin);
	freopen("necklace.out","w",stdout);
	cin>>t;
	srand(time(NULL));
	while(t--)
	{
		scanf("%lld%lld",&n,&k);
		ass=0;
		tot=0;
		all=0;
		find(n);
		sort(que+1,que+tot+1);
		for(int i=1;i<=tot;i++)
		{
			if(que[i]!=que[i-1])
			{
				another[++all]=que[i];
				num[all]=0;
			}
			num[all]++;
		}
		for(int i=1;i<=all;i++)
		{
			powe[i]=1;
			for(int j=1;j<=num[i];j++)
			powe[i]*=another[i];
		}
		dfs(0,1,1);
		printf("%lld\n",1ll*ass*fast(n%mod,mod-2,mod)%mod);
	}
	return 0;
}
/*
3
4 5
5 6
6 8
*/