【GMOJ7177】鱼跃龙门

时间:2021-07-15
本文章向大家介绍【GMOJ7177】鱼跃龙门,主要包括【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);
			g=exgcd(x,y,_,__); x/=g; y/=g;
			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;
}

原文地址:https://www.cnblogs.com/stoorz/p/15015850.html