今天做的这道题呢,总算是有两道送分题了
虽然很简单却因为空间开小了丢了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
*/