Comet OJ - Contest #10 C 鱼跃龙门(质因数分解&扩展欧几里得)
题目大意
给定一个正整数 n,一共有 n 座龙门,跳过第 j (j<n) 座龙门将会到达第 j+1座龙门前,特殊地,跳过第 n座龙门后将会到达第 1 座龙门前。
胖头鱼一开始在第一座龙门前,接下来,第 i 个时刻内它会向前跳 i 次,每次跳过 1 座龙门,求最小的正整数 x 满足第 x 个时刻结束后胖头鱼恰好会回到起点。
解题思路
设跳动次数为i,则当
n
∣
i
(
i
+
1
)
2
n|{i(i+1)\over 2}
n∣2i(i+1)时满足条件,由此得出方程
{
p
x
−
q
y
=
1
x
∗
y
=
2
n
\begin{cases} px-qy&=1\\ x*y&=2n \end{cases}
{px−qyx∗y=1=2n
最小的i即最小的qy,由此得出答案
但实际上这题卡了 n \sqrt n n的分解因数,因此需要先素数筛筛出所有的素数,然后实现 O ( n log n ) O(\frac{n}{\log n}) O(lognn)分解
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int size=1e6+5;
bool prime[size];
int p[size],tol;
void init()
{
for(int i=0;i<size;i++) prime[i]=true;
for(int i=2;i<size;i++)
{
if(prime[i]) p[++tol]=i;
for(int j=1;j<=tol&&i*p[j]<size;j++)
{
prime[i*p[j]]=false;
if(i%p[j]==0) break;
}
}
}
inline LL exgcd(LL a,LL b,LL &x,LL &y){
if(b == 0){
x = 1,y = 0;
return a;
}
LL g = exgcd(b,a%b,x,y);
LL tep = x;
x = y;
y = tep-a/b*y;
return g;
}
LL num[32],cnt[32];
int tot;
LL quick_pow(LL a,LL b){LL ans=1;while(b){if(b&1) ans=ans*a;a=a*a;b>>=1;} return ans;}
int main()
{
int t;
scanf("%d",&t);
init();
while(t--)
{
LL n;
scanf("%lld",&n);
n=n*2;
LL tmpn=n;
tot=0;
for(int i=1;i<=tol&&p[i]*p[i]<=n;i++)
{
if(n%p[i]==0)
{
num[++tot]=p[i];
cnt[tot]=0;
do{
cnt[tot]++;
n/=p[i];
}while(n%p[i]==0);
}
}
if(n!=1) {num[++tot]=n;cnt[tot]=1;}
for(int i=1;i<=tot;i++) num[i]=quick_pow(num[i],cnt[i]);
n=tmpn;
LL ans=2*n-1;
for(int i=0;i<(1<<tot);++i)
{
LL x=1;
for(int j=0;j<tot;++j)
{
if((1<<j)&i)
x=x*num[j+1];
}
LL y=n/x;
LL p,q;
LL r=exgcd(x,-y,p,q);
q/=r;
q%=x;
if(q<=0) q+=x;
ans=min(ans,q*y);
}
printf("%lld\n",ans);
}
}