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
*/