Codeforces Round #752 (Div. 2)

本文详细解析了Codeforces Round #752 (Div. 2)中的五道编程题目,包括A.Era、B.XOR Specia-LIS-t、C.Di-visible Confusion、D.Moderate Modular Mode和E.Extreme Extension。通过对题目描述、问题分析及代码实现的阐述,帮助参赛者理解解题思路和优化方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A.Era

题目描述

​ 给一个长为 n n n 的序列 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,你可以对序列做任意次如下所述的操作:选择一个数字 k k k(每次操作选择的数字可以不同),将其插入到序列 a a a 的任意位置,比如: a = [ 3 , 3 , 4 ] a=[3,3,4] a=[3,3,4] k = 2 k=2 k=2,操作后可能的序列有 [ 2 , 3 , 3 , 4 ] , [ 3 , 2 , 3 , 4 ] , [ 3 , 3 , 2 , 4 ] , [ 3 , 3 , 4 , 2 ] [2,3,3,4],[3,2,3,4],[3,3,2,4],[3,3,4,2] [2,3,3,4],[3,2,3,4],[3,3,2,4],[3,3,4,2]

​ 操作尽可能少的次数,使序列 a a a 满足: ∀ i , a i ≤ i \forall i,a_i\leq i i,aii,求这个最少的操作次数。

​ 数据范围:多组测试数据, 1 ≤ n ≤ 100 1\leq n\leq 100 1n100 1 ≤ a i ≤ 1 0 9 1\leq a_i\leq 10^9 1ai109

分析

​ 对于某个下标 i i i,如果 a i > i a_i>i ai>i,最少需要在位置 i i i 前插入 a i − i a_i-i aii 个数字。枚举所有的 a i − i a_i-i aii,其中的最大值即为答案,即 a n s = max ⁡ i = 1 n { a i } ans=\max\limits_{i=1}^{n}\{a_i\} ans=i=1maxn{ai}

代码

#include<bits/stdc++.h>

using namespace std;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            int k;
            scanf("%d", &k);
            ans = max(ans, k - i);
        }
        cout << ans << endl;
    }
}

B.XOR Specia-LIS-t

题目描述

​ 给一个长为 n n n 的序列 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,把它分成若干个不相交的子序列,第 i i i 个子序列的最长递增子序列的长度定义为 h i h_i hi。比如我们把序列 [ 2 , 5 , 3 , 1 , 4 , 3 , 2 , 2 , 5 , 1 ] [2,5,3,1,4,3,2,2,5,1] [2,5,3,1,4,3,2,2,5,1] 分割成 [ 2 , 5 , 3 , 1 , 4 ] , [ 3 , 2 , 2 , 5 ] , [ 1 ] [2,5,3,1,4],[3,2,2,5],[1] [2,5,3,1,4],[3,2,2,5],[1],则 h = [ 3 , 2 , 1 ] h=[3,2,1] h=[3,2,1]

​ 判断能否以某种方式分割 a a a 序列,使得 h h h 序列的异或和等于 0 0 0

​ 数据范围:多组测试数据, 2 ≤ n ≤ 1 0 5 2\leq n\leq 10^5 2n105 1 ≤ a i ≤ 1 0 9 1\leq a_i\leq 10^9 1ai109 n n n 的总和不超过 3 × 1 0 5 3\times 10^5 3×105

分析

​ 如果 n n n 是偶数,把 a a a 序列分割成长为 1 1 1 n n n 个子序列,答案一定是 YES

​ 如果 n n n 是奇数,我们考虑寻找满足 a i ≥ a i + 1 a_i\geq a_{i+1} aiai+1 的相邻的两个数字,把这两个数字作为一个子序列分割出来,最长递增子序列的长度为 1 1 1,如果找的到,答案为 YES,否则为 NO

代码

#include<bits/stdc++.h>

using namespace std;
int a[300010];

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        bool flag = true;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        for (int i = 1; i <= n - 1; i++)
            if (a[i] >= a[i + 1]) {
                flag = false;
            }
        if (n % 2 == 0 || flag == false) {
            puts("YES");
        } else {
            puts("NO");
        }
    }
    return 0;
}

C.Di-visible Confusion

题目描述

​ 给一个长为 n n n 的序列 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an, 进行如下操作:选择一个 a i a_i ai,要求 ( i + 1 ) ∤ a i (i+1)\nmid a_i (i+1)ai,将其从序列中移除。判断能否通过若干次操作使得序列变为空。

​ 数据范围:多组测试数据, 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105 1 ≤ a i ≤ 1 0 9 1\leq a_i\leq 10^9 1ai109 n n n 的总和不超过 3 × 1 0 5 3\times 10^5 3×105

分析

​ 对于所有的 i i i,如果 2 2 2 ~ i + 1 i+1 i+1 中至少存在一个数字不整除 a i a_i ai,则答案为 YES,否则答案为 NO

​ 使用数学归纳法来证明:如果 a n a_n an 前面的 n − 1 n-1 n1 个数字都能被移除,则 a n a_n an 也能在其位置为 $1 $ ~ n n n 中的某个位置时被移除(假设该位置为 k k k a n a_n an 可以在序列中有 k − 1 k-1 k1 个数字时被移除)。

​ 下面考虑如何快速判断 2 2 2 ~ $ i+1$ 中是否至少有一个数字不整除 a i a_i ai。如果 a i a_i ai 能被 2 2 2 ~ $ i+1$ 的所有数字整除,这代表 a i a_i ai 也能被 LCM ( 2 , 3 , ⋯   , i + 1 ) \text{LCM}(2,3,\cdots,i+1) LCM(2,3,,i+1) 整除,但是当 n = 22 n=22 n=22 时, LCM ( 2 , 3 , ⋯   , 23 ) > 1 0 9 > a i \text{LCM}(2,3,\cdots,23)>10^9>a_i LCM(2,3,,23)>109>ai。所以当 i ≥ 22 i\geq 22 i22 时,必然存在一个数字不整除 a i a_i ai,不需要逐一判断;当 i < 22 i<22 i<22 时,暴力检验。时间复杂度 O ( n + 2 1 2 ) O(n+21^2) O(n+212)

代码

#include<bits/stdc++.h>

using namespace std;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        bool flag = true;
        for (int i = 1; i <= n; i++) {
            int x;
            scanf("%d", &x);
            bool f = false;
            for (int j = i + 1; j >= 2; j--) {
                if (x % j != 0) {
                    f = true;
                    break;
                }
            }
            flag = flag & f;
        }
        if (flag == true) {
            puts("YES");
        } else {
            puts("NO");
        }
    }
    return 0;
}

D.Moderate Modular Mode

题目描述

​ 有两个偶数 x , y ( 2 ≤ x , y ≤ 1 0 9 ) x,y(2\leq x,y\leq 10^9) x,y(2x,y109),找到一个数字 n ( 2 ≤ n ≤ 1 0 18 ) n(2\leq n\leq 10^{18}) n(2n1018),使得 n m o d    x = y m o d    n n\mod x=y\mod n nmodx=ymodn

分析

​ 如果 x > y x>y x>y,则 ( x + y ) m o d    x = y m o d    x = y m o d    ( x + y ) = y (x+y)\mod x=y\mod x=y\mod (x+y)=y (x+y)modx=ymodx=ymod(x+y)=y,即 n = x + y n=x+y n=x+y

​ 如果 x ≤ y x\leq y xy

​ 结论一: n ≥ x n\geq x nx

​ 证明:使用反证法证明,存在某个 n < x n<x n<x 使得 n m o d    x = y m o d    n n\mod x=y\mod n nmodx=ymodn 成立。则 n m o d    x = n n\mod x =n nmodx=n,但 y m o d    n < n y\mod n<n ymodn<n,与假设矛盾,因此结论一正确。

​ 结论二: n ≤ y n\leq y ny

​ 证明:使用反证法证明,存在某个 n > y n>y n>y 使得 n m o d    x = y m o d    n n\mod x=y\mod n nmodx=ymodn 成立。则 n m o d    x < x n\mod x<x nmodx<x,但 y m o d    n = y ≥ x y\mod n=y\geq x ymodn=yx,与假设矛盾, 因此结论二正确。

​ 所以 x ≤ n ≤ y x \leq n \leq y xny,下面考虑如何求出 n n n 确切的值。

​ 假设有一个 X X X 轴,一开始在 0 0 0 点,从 0 0 0 点跳到 y y y 点,每次跳跃的距离为 x x x。有可能在最后一次跳跃后跳过点 y y y,设最后一次跳跃前的位置为 p = y − y m o d    x p=y-y\mod x p=yymodx,从位置 p p p 跳跃到 y y y 点需要两个步骤。因为 y − p y-p yp 是偶数,所以我们需要跳跃 y − p 2 \frac{y-p}{2} 2yp 个单位,此时跳到了位置 t = p + y − p 2 t=p+\frac{y-p}{2} t=p+2yp,可以发现 t t t 就是我们要求的 n n n,因为 t m o d    x = y − p 2 t\mod x=\frac{y-p}{2} tmodx=2yp y m o d    t = ( y − p ) − y − p 2 = y − p 2 y\mod t=(y-p)-\frac{y-p}{2}=\frac{y-p}{2} ymodt=(yp)2yp=2yp,即 n = t = y − y m o d    x 2 n=t=y-\frac{y\mod x}{2} n=t=y2ymodx

代码

#include<bits/stdc++.h>

using namespace std;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int x, y;
        cin >> x >> y;
        if (x <= y) {
            cout << y - y % x / 2 << endl;
        } else {
            cout << x + y << endl;
        }
    }
    return 0;
}

E.Extreme Extension

题目描述

​ 给一个长为 n ( 1 ≤ n ≤ 1 0 5 ) n(1\leq n\leq 10^5) n(1n105) 的序列 a 1 , a 2 , ⋯   , a n ( 1 ≤ a i ≤ 1 0 5 ) a_1,a_2,\cdots,a_n(1\leq a_i\leq 10^5) a1,a2,,an(1ai105),每次操作可以选择序列中的一个数字 a i a_i ai,将其拆分为两个数字之和并代替 a i a_i ai 加入到序列中。定义一个序列的 极值 为使得数组单调不下降所需的最少操作次数,求序列 a a a 的所有子序列的 极值 之和,答案对 998244353 998244353 998244353 取模。

分析

​ 对于 a i > a i + 1 a_i>a_{i+1} ai>ai+1 的情况,需要将 a i a_i ai 拆成 k k k 个数字: 1 ≤ b 1 ≤ b 2 ≤ ⋯ ≤ b k ≤ a i + 1 1\leq b_1\leq b_2\leq \cdots\leq b_k\leq a_{i+1} 1b1b2bkai+1 b 1 + b 2 + ⋯ + b k = a i b_1+b_2+\cdots+b_k=a_i b1+b2++bk=ai。由于 b k ≤ a i + 1 b_k\leq a_{i+1} bkai+1,所以 k ≥ ⌈ a i a i + 1 ⌉ k\geq \lceil\frac{a_i}{a_{i+1}}\rceil kai+1ai 。显然 b 1 b_1 b1 越大操作的次数就越少(比如 [ 4 , 4 , 4 , 5 ] [4,4,4,5] [4,4,4,5] [ 3 , 3 , 3 , 3 , 5 ] [3,3,3,3,5] [3,3,3,3,5] 要好),所以令 k = ⌈ a i a i + 1 ⌉ k=\lceil\frac{a_i}{a_{i+1}}\rceil k=ai+1ai。注意到 b 1 ≤ ⌊ a i k ⌋ b_1\leq \lfloor\frac{a_i}{k}\rfloor b1kai,令 b 1 = ⌊ a i k ⌋ = ⌊ a i ⌈ a i a i + 1 ⌉ ⌋ b_1=\lfloor\frac{a_i}{k}\rfloor=\Bigg\lfloor\frac{a_i}{\big\lceil\frac{a_i}{a_{i+1}}\big\rceil}\Bigg\rfloor b1=kai=ai+1aiai。经过 k − 1 k-1 k1 次操作即可将 a i a_i ai 拆分成 [ b 1 , b 2 , ⋯   , b k ] [b_1,b_2,\cdots,b_k] [b1,b2,,bk]

​ 按照如下步骤求出 极值

  • 倒序遍历 i = n − 1 i=n-1 i=n1 ~ 1 1 1
  • 答案加 ⌈ a i a i + 1 ⌉ − 1 \lceil\frac{a_i}{a_{i+1}}\rceil-1 ai+1ai1
  • a i = ⌊ a i ⌈ a i a i + 1 ⌉ ⌋ ​ a_i=\Bigg\lfloor\frac{a_i}{\big\lceil\frac{a_i}{a_{i+1}}\big\rceil}\Bigg\rfloor​ ai=ai+1aiai

时间复杂度为 O ( n ) O(n) O(n),求出所有子序列的 极值 之和的时间复杂度为 O ( n 2 ) O(n^2) O(n2),考虑如何用更优地时间复杂度解决此题。

d p ( i , x ) dp(i,x) dp(i,x) 为第 i i i 个数被拆分为最小元素为 x x x 的若干个数,而且以 x x x 为开头的非递减数列的数量。

我们只考虑 x x x 有多少种可能的值(实际只有 O ( 1 0 5 ) O(\sqrt{10^5}) O(105 ),后面会提到)。对于 x = 1 x=1 x=1 ~ 1 0 5 10^5 105,需要进行如下的状态转移: d p ( i , ⌊ a i ⌈ a i x ⌉ ⌋ ) + = d p ( i + 1 , x ) dp\Bigg(i,\Bigg\lfloor\frac{a_i}{\big\lceil\frac{a_i}{x}\big\rceil}\Bigg\rfloor\Bigg)+=dp(i+1,x) dp(i,xaiai)+=dp(i+1,x)

实际上 x x x 最多只有 2 1 0 5 2\sqrt{10^5} 2105 种不同的值( ⌊ m 1 ⌋ , ⌊ m 2 ⌋ , ⋯   , ⌊ m m ⌋ \lfloor\frac{m}{1}\rfloor,\lfloor\frac{m}{2}\rfloor,\cdots,\lfloor\frac{m}{m}\rfloor 1m,2m,,mm,整除分块的思想)。因此我们可以在 O ( n 1 0 5 ) O(n\sqrt{10^5}) O(n105 ) 的时间复杂度内解决此题,而且转移方程之和 i , i + 1 i,i+1 i,i+1 有关,可以使用滚动数组优化掉一维。

最后根据 d p dp dp 数组计算答案,对于 d p ( i + 1 , x ) dp(i+1,x) dp(i+1,x),它对答案的贡献为 i × d p ( i + 1 , x ) × ( ⌈ a i a i + ! ⌉ − 1 ) i\times dp(i+1,x)\times(\lceil\frac{a_i}{a_{i+!}}\rceil-1) i×dp(i+1,x)×(ai+!ai1),这是因为有 i × d p ( i + 1 , x ) i\times dp(i+1,x) i×dp(i+1,x) 个序列,每个序列都对 a i a_i ai 进行这样的拆分。

时间复杂度 O ( n 1 0 5 ) O(n\sqrt{10^5}) O(n105 ),空间复杂度 O ( n ) O(n) O(n)

代码

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>

using namespace std;
using namespace __gnu_pbds;
const int mod = 998244353;
int a[300010];

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        gp_hash_table<int, int> dp;
        long long ans = 0;
        for (int i = n; i >= 1; i--) {
            gp_hash_table<int, int> &&dp2 = {};
            dp2[a[i]] = 1;
            for (auto it:dp) {
                int t = (a[i] + it.first - 1) / it.first;
                dp2[a[i] / t] += it.second;
                ans = ans + ((long long) i * it.second) * (t - 1);
            }
            ans = ans % mod;
            dp = move(dp2);
        }
        cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值