文章目录
- 题目
- 题目描述
- 输入输出格式
- 数据范围
- 测试样例
- 样例说明
- 思路
- 代码
- 复杂度分析
- 时间复杂度
- 空间复杂度
题目
题目链接🔗
题目描述
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"; // 输出 NOreturn; // 结束当前测试用例}}// 如果没有余项,则可以被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)