题目
题目描述
hhuggo \text{hhuggo} hhuggo 把 kouki \text{kouki} kouki 的平板拿走了导致 kouki \text{kouki} kouki 不能打《Phigros》!他给了 kouki \text{kouki} kouki 一个整数 x x x 和一个整数数组 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an 。 kouki \text{kouki} kouki 必须确定 a 1 ! + a 2 ! + . . . + a n ! a_1!+a_2!+...+a_n! a1!+a2!+...+an! 是否能被 x ! x! x! 整除,答对了才能拿回他的平板(绝对不是因为 hhuggo \text{hhuggo} hhuggo 写不出题目而不好意思去问 kiwi \text{kiwi} kiwi 于是转而婉转地去问 kouki \text{kouki} kouki)。
众所周知,这里的 k ! k! k! 是 k k k 的阶乘,即所有小于或等于 k k k 的正整数的乘积。例如, 3 ! = 1 ⋅ 2 ⋅ 3 = 6 3! = 1 \cdot 2 \cdot 3 = 6 3!=1⋅2⋅3=6 , $5! = 1 \cdot 2 \cdot 3 \cdot 4 \cdot 5 = 120 $。
输入输出格式
【输入格式】
测试用例的第一行包含两个整数 n n n 和 x x x。
第二行包含 n n n 个整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an ,表示给定数组的元素。
【输出格式】
对于每个测试用例,输出一行。如果
a
1
!
+
a
2
!
+
.
.
.
+
a
n
!
a_1!+a_2!+...+a_n!
a1!+a2!+...+an! 能被
x
!
x!
x! 整除,则打印 “YES”(不带引号),否则打印 “NO”(不带引号)。
注意答案输出的大小写问题,答案仅接受全大写的“YES"和“NO”。
数据范围
1 ≤ n ≤ 5 × 1 0 5 1\le n\le 5 \times 10^5 1≤n≤5×105
1 ≤ x ≤ 5 × 1 0 5 1 \le x \le 5 \times 10^5 1≤x≤5×105
1 ≤ a i ≤ x 1 \le a_i \le x 1≤ai≤x
测试样例
6 4
3 2 2 2 3 3
YES
10 5
4 3 2 1 4 3 2 4 3 4
NO
样例说明
在第一个样例中, 3 ! + 2 ! + 2 ! + 2 ! + 3 ! + 3 ! = 24 3!+2!+2!+2!+3!+3!=24 3!+2!+2!+2!+3!+3!=24,而且 4 ! = 24 4!=24 4!=24,所以输出 YES 。
思路
简明题意
给出一个长度为 n n n 的数组 a a a ,给出一个正整数 x x x ,保证数组 a a a 里面的元素都是正整数。问 a 1 ! + a 2 ! + a 3 ! . . . + a n ! a_1! + a_2! + a_3!... +a_n! a1!+a2!+a3!...+an! 是否能被 x ! x! x! 整除。
对于
1
<
k
<
x
1<k<x
1<k<x,我们需要把
k
!
k!
k! 向
(
k
+
1
)
!
(k+1)!
(k+1)! 合并,合并后可能会产生一些无法合并的余项。
这些余项最多是
(
x
−
1
)
∗
(
x
−
1
)
!
+
(
x
−
2
)
∗
(
x
−
2
)
!
+
.
.
.
+
1
∗
1
!
(x-1)*(x -1)!+(x-2)*(x-2)!+...+1*1!
(x−1)∗(x−1)!+(x−2)∗(x−2)!+...+1∗1! 而
x
!
=
(
x
−
1
)
∗
(
x
−
1
)
!
+
(
x
−
2
)
∗
(
x
−
2
)
!
+
.
.
.
+
1
∗
1
!
+
1
x!=(x-1)*(x-1)!+(x-2)*(x-2)!+...+1*1!+1
x!=(x−1)∗(x−1)!+(x−2)∗(x−2)!+...+1∗1!+1所以余项一定严格小于x!,所以只要有余项,就输出”NO”,否则就是YES”
代码
#include <iostream>
using namespace std;
const int N = 5e5 + 10; // 数组大小
int n, x, a, num[N]; // n为乘客数,x为判断的目标阶乘,num数组用于记录每个数出现的次数
void solve() {
cin >> n >> x; // 输入数组大小n和目标阶乘x
// 初始化num数组
for (int i = 1; i <= n; i++) {
cin >> a; // 输入每个数组元素
num[a]++; // 记录该元素出现的次数
}
// 从1到x-1检查是否能合并至 x!
for (int i = 1; i < x; i++) {
num[i+1] += num[i] / (i + 1); // 将i!合并到(i+1)!
num[i] %= (i + 1); // 计算当前余项
if (num[i]) { // 如果有余项,说明无法合并至x!
cout << "NO\n"; // 输出 NO
return; // 结束当前测试用例
}
}
// 如果没有余项,则可以被x!整除
cout << "YES\n";
}
int main() {
int T = 1;
while (T--)
solve();
return 0;
}
复杂度分析
时间复杂度
输入部分:读取每个测试用例的数据需要
O
(
n
)
O(n)
O(n) 时间,最多有
n
n
n 个元素。
合并和检查部分:对于每个数组元素,我们最多执行
x
x
x 次合并操作,每次合并操作的复杂度为常数
O
(
1
)
O(1)
O(1)。
总体时间复杂度:由于每个测试用例的处理时间是
O
(
n
+
x
)
O(n + x)
O(n+x),而根据题目中的数据范围,最大
n
n
n 和
x
x
x 都为
5
×
1
0
5
5 \times 10^5
5×105,所以总的时间复杂度为
O
(
n
+
x
)
O(n + x)
O(n+x),即
O
(
1
0
5
)
O(10^5)
O(105),对于所有测试用例,时间复杂度为
O
(
∑
i
=
1
(
n
i
+
x
i
)
)
O(\sum_{i=1}(n_i+x_i))
O(∑i=1(ni+xi))
空间复杂度
我们使用了一个大小为
N
N
N 的数组
n
u
m
[
]
num[]
num[] 来记录每个元素的出现次数,大小为
O
(
N
)
O(N)
O(N)。
所以空间复杂度为
O
(
N
)
O(N)
O(N),其中
N
N
N 为题目给定的最大值(在此问题中最大为
5
×
1
0
5
5 \times 10^5
5×105)