题目描述
给定一个正整数
n
n
n,一共有
n
n
n 座龙门,跳过第
j
(
j
<
n
)
j (j < n)
j(j<n) 座龙门将会到达第
j
+
1
j+1
j+1 座龙门前,特殊地,跳过第
n
n
n 座龙门后将会到达第
1
1
1 座龙门前。
胖头鱼一开始在第一座龙门前,接下来,第
i
i
i 个时刻内它会向前跳
i
i
i 次,每次跳过
1
1
1 座龙门,求最小的正整数
x
x
x 满足第
x
x
x 个时刻结束后胖头鱼恰好会回到起点。
输入格式
第一行一个整数
T
T
T,表示数据组数。
接下来
T
T
T 行,每行一个整数
n
n
n,表示一共有
n
n
n 座龙门。
输出格式
一共 T T T 行,每行一个整数 x x x,表示答案。
输入样例1
5
2
4
6
8
10
输出样例1
3
7
3
15
4
样例解释1
对
n
=
10
n=10
n=10 的样例解释:
一开始在
1
1
1 的前面也就是位置
0
0
0,
第一时刻跳一步到
(
0
+
1
)
m
o
d
n
=
1
(0+1)\bmod n = 1
(0+1)modn=1,
第二时刻跳两步到
(
1
+
2
)
m
o
d
n
=
3
(1+2)\bmod n = 3
(1+2)modn=3,
第三时刻跳三步到
(
3
+
3
)
m
o
d
n
=
6
(3+3)\bmod n = 6
(3+3)modn=6
第四时刻跳四步到
(
6
+
4
)
m
o
d
n
=
0
(6+4)\bmod n = 0
(6+4)modn=0
第四时刻后恰好跳回原点
0
0
0,所以答案为
4
4
4。
输入样例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
≤
10
1 \leq T \leq 10, 1 \leq n \leq 10
1≤T≤10,1≤n≤10
对于全部的数据
1
≤
T
≤
100
,
1
≤
n
≤
1
0
12
1 \leq T \leq 100, 1 \leq n \leq 10^{12}
1≤T≤100,1≤n≤1012
题目解答
这题初看起来很像扩欧,像我这样的蒟蒻就像直接套板子了。但可惜的是,虽然这道题需要使用扩欧,可并不能直接使用,而是要经过一系列复杂的变换才可以。那我们就开始题解部分:
- 首先,根据求和公式,容易发现就是求式子 n ∣ 1 2 × x × ( x + 1 ) n \mid \frac{1}{2} \times x \times (x + 1) n∣21×x×(x+1) 的最小正整数解 x x x
- 接着两边同时乘二,得到 2 n ∣ x × ( x + 1 ) 2n \mid x \times (x + 1) 2n∣x×(x+1)
显然,我们不可能使用二次公式计算,所以要换一种方法。由于
x
x
x 和
x
+
1
x + 1
x+1 互质,所以我们可以尝试对
2
n
2n
2n 进行质因数分解:
2
n
=
∏
i
=
1
s
p
i
k
i
2n = \prod_{i = 1}^{s} p_i^{k_i}
2n=i=1∏spiki
- 其中每个 p i p_i pi 都是质数,必定与 p j ( i ≠ j ) p_j (i \neq j) pj(i=j) 互质,所以只要对它的每一个质因子进行枚举子集即可得到两个乘积为 n n n 且互质的数
虽然枚举两个数的乘积可以被
n
n
n 整除且互质,但它们的差值不一定为
1
1
1。并不一定能构成
x
×
(
x
+
1
)
x \times (x + 1)
x×(x+1) 的形式,所以这时候,扩展欧几里得就派上了用场。若我们当前在质因数中枚举出的两个数为
a
,
b
a, b
a,b,就可以把它转换成以下式子:
a
p
−
b
q
=
1
ap - bq = 1
ap−bq=1
- 在这里,我们将 a a a 与 b b b 扩大一定倍数后去满足它们的差为 1 1 1 这一性质。 a p ap ap 表示的就是 x + 1 x + 1 x+1,而 b q bq bq 相对应的就为 x x x 了,恰好就是一个扩欧标准的方程
- 由于 n ∣ a × b n \mid a \times b n∣a×b ,那么 n ∣ a p × b q n \mid ap \times bq n∣ap×bq,方程里的值满足能将 n n n 整除。
不过还要注意两点:
- 因为扩展欧几里得求解的是 a x + b y = c ax + by = c ax+by=c,与上面的方程符号相反,而题目要求的是最小的正整数,所以求解的应该是 b b b 的最大负整数解
- 由于题目里的数据较大,直接分解质因数可能超时,所以应该使用筛法加速这一过程
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{}
^⌣^