• AcWing第57场周赛


    题目列表

    A AcWing 4485. 比大小

    题目描述

    给定一个长度为 n 的数组 a1,a2,…,an 和一个长度为 n 的数组 b1,b2,…,bn。
    请你计算,数组 a 的所有元素之和是否大于或等于数组 b 的所有元素之和。
    输入格式
    第一行包含整数 n。
    第二行包含 n 个整数 a1,a2,…,an。
    第三行包含 n 个整数 b1,b2,…,bn。
    输出格式
    如果数组 a 的所有元素之和大于或等于数组 b 的所有元素之和,则输出 Yes,否则输出 No。
    数据范围
    前三个测试点满足 1≤n≤5。
    所有测试点满足 1≤n≤50,0≤ai,bi≤1000。
    输入样例1:
    5
    1 2 3 4 5
    2 1 4 3 5
    输出样例1:
    Yes
    输入样例2:
    5
    1 1 1 1 1
    1 0 1 0 1
    输出样例2:
    Yes
    输入样例3:
    3
    2 3 9
    1 7 9
    输出样例3:
    No

    分析

    签到题,将a数组之和减去b数组之和与0比较即可。

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int main(){
        int n,x;
        scanf("%d",&n);
        int res = 0;
        for(int i = 0;i < n;i++) {
            scanf("%d",&x);
            res += x;
        }
        for(int i = 0;i < n;i++) {
            scanf("%d",&x);
            res -= x;
        }
        if(res >= 0)    puts("Yes");
        else    puts("No");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    B AcWing 4486. 数字操作

    题目描述

    给定一个整数 n,你可以对该数进行任意次(可以是 0 次)变换操作。
    每次操作为以下两种之一:
    将整数 n 乘以任意一个正整数 x。
    将整数 n 替换为 n√(执行此操作的前提是 n√ 为整数)。
    请你计算,通过上述操作,n 能达到的最小可能值,以及达到最小可能值所需要的最少操作次数。
    输入格式
    一个整数 n。
    输出格式
    一行,两个整数,分别为 n 能达到的最小可能值,以及达到最小可能值所需要的最少操作次数。
    数据范围
    所有测试点满足 1≤n≤106
    输入样例1:
    20
    输出样例1:
    10 2
    输入样例2:
    5184
    输出样例2:
    6 4

    分析

    本题考察算术基本定理和质因数分解。对于任意一个正整数n一定可以拆分为若干个质数相乘,即n = p1a1 * p2a2 * … * pkak。其中pi表示质数。对于n,乘上正整数x的操作会使得n增大,所以为了减小n的值只有依靠开方操作,而只有在n是平方数的前提下才能对n开方,所以乘法操作的作用就是将n变成平方数。

    首先开方操作对于写成质因子相乘式子的n而言,等价于将每个质数的幂都减半,比如36 = 22 * 32,对36开方就得到6 = 21 * 31。换而言之,如果n是平方数,那么n根据算术基本定理的展开式中每个质数的幂次一定是偶数的,否则无法开方。

    周赛时我想到的第一个解法就是模拟,比如n为5184时,一开始就是平方数,开平方得到72 = 23 * 32,由于2的幂是3,所以不是平方数,我们给n乘上2变成144,这样2的幂就变成了4,可以继续开方得到12 = 22 * 3,乘上3再次开方得到6,6只含2,3两个质因子,并且幂都是1,不能继续减小了。这也可以得出本题的另一个性质,n经过若干次操作后得到的最小可能值一定是n的所有质因子之积。

    上面的模拟过程开了3次平方根,乘法操作也有2次,一共是5次操作,但是最少操作次数却是4次,这是因为乘法操作可以合并为一次,开方操作需要满足n是平方数的条件,但是乘法操作没有条件,只要一开始将n的所有质因子的幂都提高到某个2的整数次方,后面只需要开方操作就可以实现目的了。

    还是以5184为例,5184 = 26 * 34,最高次幂是6,需要将其提升到最近的2的整数次幂8,3的幂也提升到8,也就是第一步将n乘上 22 * 34,然后对新的n连续开方3次就得到了最终结果6了,这样操作的最小次数就是4次。

    本题虽然只用到了数论中的简单知识,但是周赛时完全推出本题的性质并且写出来也是不容易的。梳理下本题的解题步骤:首先要对n分解质因数,能够化成的最小可能值就是n的所有质因子之积。然后就是求出n的质因子中最大的幂,如果它不是2的整数次方,就要将其提升到比它大的最小的2的整数次方。具体乘上多少不需要我们考虑,但是需要判断一开始n的质因子的幂是否都相等并且等于2的整数次幂,如果是,就不需要做第一次乘法操作了。然后加上开方的次数就是最小的操作次数了。

    代码

    #include <iostream>
    #include <vector>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    vector<int> cnt;
    int main(){
        int n;
        scanf("%d",&n);
        int res = 1,m = 0;
        for(int i = 2;i * i <= n;i++){
            if(n % i == 0){
                res *= i;
                int t = 0;
                while(n % i == 0){//统计质因子的幂
                    t++;
                    n /= i;
                }
                cnt.push_back(t);
                while(1 << m < t)   m++;//统计不小于幂的最小2的整数次幂,比较巧妙
            }
        }
        if(n > 1){//剩下的最大的质因子
            res *= n;
            cnt.push_back(1);
            while(1 << m < 1)   m++;
        }
        for(auto x : cnt){
            if(x < 1 << m){//如果有幂不等于2^m的幂,就需要加上乘法操作
                m++;
                break;
            }
        }
        cout<<res<<" "<<m<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    C AcWing 4487. 最长连续子序列

    题目描述

    给定一个长度为 n 的整数序列 a1,a2,…,an。
    现在,请你找到一个序列 a 的连续子序列 al,al+1,…,ar,要求:
    al + al+1 + … + ar>100×(r−l+1)。
    连续子序列的长度(即 r−l+1)尽可能大。
    请你输出满足条件的连续子序列的最大可能长度。
    输入格式
    第一行包含整数 n。
    第二行包含 n 个整数 a1,a2,…,an。
    输出格式
    一个整数,表示最大可能长度。
    如果满足条件的连续子序列不存在,则输出 0。
    数据范围
    前三个测试点满足 1≤n≤5。
    所有测试点满足 1≤n≤106,0≤ai≤5000。
    输入样例1:
    5
    100 200 1 1 1
    输出样例1:
    3
    输入样例2:
    5
    1 2 3 4 5
    输出样例2:
    0
    输入样例3:
    2
    101 99
    输出样例3:
    1

    分析

    首先看下题目中的单调性。

    如果用f[i]表示以i开头的满足条件的连续子序列的最大可能,假设这个最长子序列长度是n,那么从i出发,长度为n - 1的子序列是否一定满足条件呢?显然是不一定的,比如1 300,最长连续是子序列1 300,但是去掉300后的子序列1的平均值是小于100的,不满足条件。

    如果我们直接二分最大长度呢?如果不存在满足条件的长度为n的子序列,是否一定不存在满足条件长度为n + 1的子序列,也是不一定的,比如102 97 102,长度为2的子序列都是不满足条件的,如果我们二分长度到2发现不满足条件,就减少长度,但是实际上长度为3的子序列102 97 102的平均值是大于100的,满足条件,所以这里也没有单调性。

    再来考虑本题能否使用01分数规划来求解呢?01分数规划问题一般是求一个分数表达式的最值问题,比如AcWing 234 放弃测试AcWing 361 观光奶牛这两个问题。本题虽然不具有分数规划问题的典型特征,但是也不妨碍我们对原表达式进行变形,用类似的方法来求解。al + al+1 + … + ar > 100×(r−l+1)变形得到∑(ai - 100)> 0,显然我们可以令bi = ai - 100,只要bi的连续子序列之和大于0,那么这个连续子序列就是满足条件的。由于本题不是求分数的最值,所以变形后仍然不具备单调性,对表达式进行变形仅仅是为了简化运算,我们需要人为的构造单调性。

    现在我们要对b数组求满足条件的最长连续子序列的长度,可以先求下b数组的前缀和s,正常情况下我们要求以i为末尾的最长连续子序列的长度,需要枚举子序列的开头,逐步增加序列的左端点,直到满足条件,不妨列出来看一下:

    s0, s1, s2, …,si-1

    我们先判断si - s0是否大于0,如果是,则最大长度就是i,不是则接着判断si - s1,可以发现,对于固定的si,如果想要si - sj最大,需要sj最小,也就是左端点的值sj越小,si - sj越可能大于0,如果s0 < s1,那么s1必然不可能是最优解,因为在si - s0 > 0时,满足条件,找到了长度为i的合法序列,那么长度为i - 1的合法子序列必然表示最优解了;在si - s0 <= 0时,由于s1>s0,则si - s1 < si - s0<=0,也不是合法的解,所以从s0到si-1我们只需要一路保存一些递减的sj即可,也就是使用单调栈来保存si左边从左到右递减的sj。

    我们来看下遍历到b数组中第i个元素时的情况,首先与栈顶元素比较下,如果s[i]大于栈顶元素,那么i不可能作为后面元素最长子序列的左端点了,也就不需要加入栈中,但是既然s[i]大于栈顶元素,说明在栈中可以找到使得si - sj > 0的j了,这时候我们就可以使用二分法来查找最长子序列的左端点了,如果在栈中二分到的sj满足条件,说明最优解的长度不小于此时的子序列的长度,如果二分到的sj不满足条件,那么由于栈中元素是递减的,栈中左边的元素也一定不满足条件,直接向右查找即可。

    本题的难点在于即使是使用分数规划的方法变形后得到的新数组b依然不具备单调性,需要继续构造前缀和才能发现单调性。

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 1000005;
    int n,a[N],stk[N];
    long long s[N];
    int main(){
        scanf("%d",&n);
        for(int i = 1;i <= n;i++) {
            scanf("%d",&a[i]);
            s[i] = s[i-1] + a[i] - 100;
        }
        int tt = -1,res = 0;
        stk[++tt] = 0;
        for(int i = 1;i <= n;i++){
            if(s[i] < s[stk[tt]]) stk[++tt] = i;
            else if(s[i] > s[stk[tt]]){
               int l = 0,r = tt;
                while(l < r){
                    int mid = l + r >> 1;
                    if(s[i] - s[stk[mid]] > 0)  r = mid;
                    else    l = mid + 1;
                }
                res = max(res,i - stk[l]); 
            }
        }
        cout<<res<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 相关阅读:
    Ajax相关知识点
    Lua多脚本执行
    PPP开源软件GMAP测试记录及原始数据比较
    java中的多态
    蓝桥杯python考级整理
    Jenkins(6)流水线(pipeline)、Jenkinsfile设置、多分支构建及简单总结
    RISC-V架构下 FPU Context 的动态保存和恢复
    Java Double equals()方法具有什么功能呢?
    【2023年11月第四版教材】软考高项极限冲刺篇笔记(2)
    Snipaste安装及使用方法
  • 原文地址:https://blog.csdn.net/qq_30277239/article/details/125471209