1. 程式人生 > >NOI模擬賽 -- 塵封的花環

NOI模擬賽 -- 塵封的花環

今天做的這道題呢,總算是有兩道送分題了

雖然很簡單卻因為空間開小了丟了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
*/