【GMOJ7177】鱼跃龙门
题目
题目链接:https://gmoj.net/senior/#main/show/7177
求一个最小的 \(k\) 使得 \(\left(\sum^{k}_{i=1}i\right)\bmod n=0\)。多测。
\(Q\leq 100,n\leq 10^{12}\)。
思路
也就是找一个最小的 \(k\) 使得 \(2n|k(k+1)\)。下面令 \(n\gets 2n\)。
那么一定是将 \(n\) 分解成 \(n=a\times b\),然后让 \(a\) 是 \(k\) 的因子,\(b\) 是 \(k+1\) 的因子。因为 \(k\) 和 \(k+1\) 互质,所以 \(n\) 的相同质因子一定是给到 \(a,b\) 中同一个数。
由于前 \(13\) 个质数相乘就已经超过 \(10^{12}\),所以 \(n\) 不同质因子数量最多就是 \(12\)。可以先把 \(10^6\) 以内的质数筛出来,然后 \(2^k\) 枚举 \(n\) 的 \(k\) 个质因子分别扔到哪一边。
把 \(n\) 分解成 \(a\times b\) 后,我们需要找到 \(a,b\) 的倍数让他们的差为 \(1\)。也就是解 \(ax+by=1\) 的最小整数解。直接上 exgcd 即可。
时间复杂度 \(O(Q(m+2^k\log n))\),其中 \(m\) 是 \(10^6\) 以内质数数量 \(\leq 88000\),\(k\) 是不同质因子数量 \(\leq 12\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010;
const ll Inf=4e18;
int Q,m,prm[N];
bool v[N];
ll n,nn,ans;
vector<ll> d;
void findprm(int n)
{
for (int i=2;i<=n;i++)
{
if (!v[i]) prm[++m]=i;
for (int j=1;j<=m;j++)
{
if (i>n/prm[j]) break;
v[i*prm[j]]=1;
if (!(i%prm[j])) break;
}
}
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if (!b) { x=1; y=0; return a; }
ll d=exgcd(b,a%b,x,y);
ll z=x; x=y; y=z-y*(a/b);
return d;
}
int main()
{
findprm(1000000);
scanf("%d",&Q);
while (Q--)
{
scanf("%lld",&n);
nn=n=2LL*n; ans=n-1;
d.clear();
for (int i=1;i<=m;i++)
if (!(nn%prm[i]))
{
ll res=1;
while (!(nn%prm[i])) nn/=prm[i],res*=prm[i];
d.push_back(res);
}
if (nn>1) d.push_back(nn);
int len=d.size(),lim=(1<<len);
for (int s=0;s<lim;s++)
{
ll v1=1,v2=1,x,y;
for (int i=0;i<len;i++)
if (s&(1<<i)) v1*=d[i];
else v2*=d[i];
ll g=exgcd(v1,v2,x,y);
if (x<0) ans=min(ans,-1LL*x*v1),ans=min(ans,y*v2);
if (y<0) ans=min(ans,-1LL*y*v2),ans=min(ans,x*v1);
}
cout<<ans<<"\n";
}
return 0;
}

浙公网安备 33010602011771号