1 条题解

  • 0
    @ 2025-8-24 21:15:08

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar FallingFYC_
    但我仍然渴望在每一次追忆之旅中留下闲暇时间,在一个场景前驻足,在岁月的朦胧里瞭望过去的自己,感受尽可能多的甜蜜。

    搬运于2025-08-24 21:15:07,当前版本为作者最后更新于2023-07-10 11:32:44,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    原题

    细节决定成败。


    分析&思路

    要先明确一点:操作 2 就是让求出序列 aa 中的正整数之和(因为如果是负数则会让结果变小)。


    一个暴力的做法是:

    • 对于操作 1,用一个变量记录所需加上的数字之和。
    • 对于操作 2,遍历一遍序列 aa,将其中的每一项加上在当前操作之前所需加上的数字之和,判断这个数是否大于 00,是则在最终结果中加上此数,否则不加。

    最坏情况下,这种方法的时间复杂度为 O(mn)O(mn)

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n , m , a[500005] , opt , k;
    long long add , ans;
    int main()
    {
        cin >> n >> m;
        for (int i = 1 ; i <= n ; i++) cin >> a[i];
        while (m--)
        {
            cin >> opt;
            if (opt == 1) {cin >> k; add += k;}
            else
            {
                ans = 0;
                for (int i = 1 ; i <= n ; i++)
                    if (a[i] + add > 0) ans += a[i] + add;
                cout << ans << endl;
            }
        }
        return 0; 
    }
    

    评测记录

    不出所料,T 了 6 个点。

    考虑优化。


    操作 1 无需优化,来看操作 2。

    对于操作 2,很容易想到一种基于暴力的优化:

    1. 对序列 aa 排序。
    2. 二分查找第一个大于 0 的数的下标,计为 pp
    3. 累加序列 aa 中从下标为 pp 的数字到最后一个(预处理前缀和)。

    这样优化的时间复杂度最坏为 O(mlogn)O(m \log n),空间复杂度为 O(n)O(n)

    注意:

    1. long long
    2. pp 要初始化成 n+1n + 1(如果没有比 00 大的数就会输出 00)。
    3. 本题中所用的前缀和是倒序的。

    代码:

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    int n , m , a[500005] , opt , k , sum[500005] ,  add , ans;
    signed main()
    {
        cin >> n >> m;
        for (int i = 1 ; i <= n ; i++) cin >> a[i];
        sort(a + 1 , a + n + 1);
        for (int i = n ; i >= 1 ; i--) sum[i] = a[i] + sum[i + 1];//倒序的前缀和
        while (m--)
        {
            cin >> opt;
            if (opt == 1) {cin >> k; add += k;}
            else
            {
                int p = n + 1;//这里的初始化很关键!
                int l = 1 , r = n , mid;
                while (l <= r)
                {
                    mid = (l + r) / 2;
                    if (a[mid] + add > 0) {p = mid; r = mid - 1;}
                    else l = mid + 1;
                }
                ans = sum[p] + (n - p + 1) * add;
                //最后将大于0的数统一加上在当前操作之前所需加上的数字之和。
                cout << ans << endl;
            }
        }
        return 0; 
    }
    

    评测记录

    • 1

    信息

    ID
    8509
    时间
    1000ms
    内存
    128MiB
    难度
    2
    标签
    递交数
    0
    已通过
    0
    上传者