题目描述
给定一个正整数 nnn,一共有 nnn 座龙门,跳过第 j(j<n)j (j < n)j(j<n) 座龙门将会到达第 j+1j+1j+1 座龙门前,特殊地,跳过第 nnn 座龙门后将会到达第 111 座龙门前。
胖头鱼一开始在第一座龙门前,接下来,第 iii 个时刻内它会向前跳 iii 次,每次跳过 111 座龙门,求最小的正整数 xxx 满足第 xxx 个时刻结束后胖头鱼恰好会回到起点。
输入格式
第一行一个整数 TTT,表示数据组数。
接下来 TTT 行,每行一个整数 nnn,表示一共有 nnn 座龙门。
输出格式
一共 TTT 行,每行一个整数 xxx,表示答案。
输入样例1
5
2
4
6
8
10
输出样例1
3
7
3
15
4
样例解释1
对 n=10n=10n=10 的样例解释:
一开始在 111 的前面也就是位置 000,
第一时刻跳一步到 (0+1) mod n=1(0+1)\bmod n = 1(0+1)modn=1,
第二时刻跳两步到 (1+2) mod n=3(1+2)\bmod n = 3(1+2)modn=3,
第三时刻跳三步到 (3+3) mod n=6(3+3)\bmod n = 6(3+3)modn=6
第四时刻跳四步到 (6+4) mod n=0(6+4)\bmod n = 0(6+4)modn=0
第四时刻后恰好跳回原点 000,所以答案为 444。
输入样例2
10
200479710
1041705379
766770747
257088468
877586977
86834214
757618747
884911150
388001368
494728090
输出样例2
30464759
9427197
74899308
9020648
877586976
43417107
96416647
212378675
43718463
36697240
数据范围
对于 10%10\%10% 的数据 1≤T≤10,1≤n≤101 \leq T \leq 10, 1 \leq n \leq 101≤T≤10,1≤n≤10
对于全部的数据 1≤T≤100,1≤n≤10121 \leq T \leq 100, 1 \leq n \leq 10^{12}1≤T≤100,1≤n≤1012
题目解答
这题初看起来很像扩欧,像我这样的蒟蒻就像直接套板子了。但可惜的是,虽然这道题需要使用扩欧,可并不能直接使用,而是要经过一系列复杂的变换才可以。那我们就开始题解部分:
- 首先,根据求和公式,容易发现就是求式子 n∣12×x×(x+1)n \mid \frac{1}{2} \times x \times (x + 1)n∣21×x×(x+1) 的最小正整数解 xxx
- 接着两边同时乘二,得到 2n∣x×(x+1)2n \mid x \times (x + 1)2n∣x×(x+1)
显然,我们不可能使用二次公式计算,所以要换一种方法。由于 xxx 和 x+1x + 1x+1 互质,所以我们可以尝试对 2n2n2n 进行质因数分解:
2n=∏i=1spiki
2n = \prod_{i = 1}^{s} p_i^{k_i}
2n=i=1∏spiki
- 其中每个 pip_ipi 都是质数,必定与 pj(i≠j)p_j (i \neq j)pj(i=j) 互质,所以只要对它的每一个质因子进行枚举子集即可得到两个乘积为 nnn 且互质的数
虽然枚举两个数的乘积可以被 nnn 整除且互质,但它们的差值不一定为 111。并不一定能构成 x×(x+1)x \times (x + 1)x×(x+1) 的形式,所以这时候,扩展欧几里得就派上了用场。若我们当前在质因数中枚举出的两个数为 a,ba, ba,b,就可以把它转换成以下式子:
ap−bq=1
ap - bq = 1
ap−bq=1
- 在这里,我们将 aaa 与 bbb 扩大一定倍数后去满足它们的差为 111 这一性质。apapap 表示的就是 x+1x + 1x+1,而 bqbqbq 相对应的就为 xxx 了,恰好就是一个扩欧标准的方程
- 由于 n∣a×bn \mid a \times bn∣a×b ,那么 n∣ap×bqn \mid ap \times bqn∣ap×bq,方程里的值满足能将 nnn 整除。
不过还要注意两点:
- 因为扩展欧几里得求解的是 ax+by=cax + by = cax+by=c,与上面的方程符号相反,而题目要求的是最小的正整数,所以求解的应该是 bbb 的最大负整数解
- 由于题目里的数据较大,直接分解质因数可能超时,所以应该使用筛法加速这一过程
AC代码
#include <bits/stdc++.h>
using namespace std;
long long t, n, ans, pos, x, y, idx, cntp[1000005], primes[1000005];
bool nPrime[1000005];
void euler(int n)
{
for (int i = 2; i <= n; i++)
{
if (!nPrime[i])
primes[++pos] = i;
for (int j = 1; j <= pos && i * primes[j] <= n; j++)
{
nPrime[i * primes[j]] = true;
if (i % primes[j] == 0)
break;
}
}
}
long long exgcd(long long a, long long b, long long &x, long long &y)
{
if (!b)
{
x = y = 1;
return a;
}
long long d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
void dfs(int cur, long long a, long long b)
{
if (cur > idx)
{
exgcd(a, b, x, y);
y = -y % a; // 取最大负整数解
if (y <= 0) // 解仍然可能小于等于0,应该加上a
y += a;
ans = min(ans, y * b); // y * b就为答案x,用结果更新ans
return;
}
dfs(cur + 1, a * cntp[cur], b);
dfs(cur + 1, a, b * cntp[cur]);
}
int main()
{
euler(1e6);
scanf("%lld", &t);
while (t--)
{
scanf("%lld", &n);
n <<= 1, ans = LONG_LONG_MAX, idx = 0;
for (int i = 1; i <= pos && primes[i] * primes[i] <= n; i++) // 质因数分解,使用筛法加速
if (n % primes[i] == 0)
{
cntp[++idx] = 1; // cntp数组用来存每个pi^ki
while (n % primes[i] == 0)
{
n /= primes[i];
cntp[idx] *= primes[i];
}
}
if (n > 1)
cntp[++idx] = n;
dfs(1, 1, 1);
printf("%lld\n", ans);
}
return 0;
}
本期博客就到这里了,若注解有误,还请跟为大佬多多指教。
如果觉得写得好的话,还可以点赞 + 收藏哦 ^⌣^\hat{} \smile \hat{}^⌣^