【QED】kouki与阶乘之间的那些事?

题目

题目链接🔗

题目描述

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!=123=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 1n5×105

1 ≤ x ≤ 5 × 1 0 5 1 \le x \le 5 \times 10^5 1x5×105

1 ≤ a i ≤ x 1 \le a_i \le x 1aix

测试样例

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! (x1)(x1)!+(x2)(x2)!+...+11! 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!=(x1)(x1)!+(x2)(x2)!+...+11!+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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想要AC的dly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值