题目
题目链接:https://gmoj.net/senior/#main/show/7177
求一个最小的 (k) 使得 (left(sum^{k}_{i=1}i
ight)mod n=0)。多测。
(Qleq 100,nleq 10^{12})。
思路
也就是找一个最小的 (k) 使得 (2n|k(k+1))。下面令 (ngets 2n)。
那么一定是将 (n) 分解成 (n=a imes 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 imes b) 后,我们需要找到 (a,b) 的倍数让他们的差为 (1)。也就是解 (ax+by=1) 的最小整数解。直接上 exgcd 即可。
时间复杂度 (O(Q(m+2^klog 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<<"
";
}
return 0;
}