【线段树】1108E2 Array and Segments (Hard version) & 1108E1 Array and Segments (Easy version)

本文介绍了一种算法,通过选择特定的区间操作来最大化一个整数序列的极差(最大值与最小值之差)。该算法适用于给定的区间操作限制,能够在易版和难版的不同数据规模下运行。核心思想是在保持最大值不变的同时,尽可能减小区间内的最小值,从而增加极差。

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

The only difference between easy and hard versions is a number of elements in the array.

You are given an array a consisting of n integers. The value of the i-th element of the array is ai.

You are also given a set of m segments. The j-th segment is [lj;rj], where 1≤lj≤rj≤n.

You can choose some subset of the given set of segments and decrease values on each of the chosen segments by one (independently). For example, if the initial array a=[0,0,0,0,0] and the given segments are [1;3] and [2;4] then you can choose both of them and the array will become b=[−1,−2,−2,−1,0].

You have to choose some subset of the given segments (each segment can be chosen at most once) in such a way that if you apply this subset of segments to the array a and obtain the array b then the value在这里插入图片描述will be maximum possible.

Note that you can choose the empty set.

If there are multiple answers, you can print any.

If you are Python programmer, consider using PyPy instead of Python when you submit your code.

Input

The first line of the input contains two integers n and m (1≤n≤105,0≤m≤300) — the length of the array a and the number of segments, respectively.

The second line of the input contains n integers a1,a2,…,an (−106≤ai≤106), where ai is the value of the i-th element of the array a.

The next m lines are contain two integers each. The j-th of them contains two integers lj and rj (1≤lj≤rj≤n), where lj and rj are the ends of the j-th segment.

Output
In the first line of the output print one integer d— the maximum possible value maxi=1nbi−mini=1nbi if b is the array obtained by applying some subset of the given segments to the array a.

In the second line of the output print one integer q (0≤q≤m) — the number of segments you apply.

In the third line print q distinct integers c1,c2,…,cq in any order (1≤ck≤m) — indices of segments you apply to the array a in such a way that the value maxi=1nbi−mini=1nbi of the obtained array b is maximum possible.

If there are multiple answers, you can print any.

Input
5 4
2 -2 3 1 2
1 3
4 5
2 5
1 3

Output
6
2
1 4

Input
5 4
2 -2 3 1 4
3 5
3 4
2 4
2 5

Output
7
2
2 3

Input
1 0
1000000

Output
0
0

Note

In the first example the obtained array b will be [0,−4,1,1,2] so the answer is 6.
In the second example the obtained array b will be [2,−3,1,−1,4] so the answer is 7.

In the third example you cannot do anything so the answer is 0.

题意

  • 给定一个长度为n的序列,和m个区间。
  • 对一个区间的操作是:对整个区间的数-1
  • 可以选择任意个区间(可以为0个、每个区间最多被选择一次)进行操作后,要求最大化的序列极差(极差即最大值 - 最小值)。
  • easy version的范围是(1≤n≤300,0≤m≤300)
  • hard version的范围是(1≤n≤1e5,0≤m≤300)

思路

  • 操作是对区间内的数减1,因此最大值不会变的更大,要使得极差变大,只能够尽量使最小值变小。
  • 如果对一个区间进行操作,可能会有以下情况:
    1.这个区间只包含最小值,那么最小值变小,极差变大。 这是我们需要的。
    2.这个区间只包含最大值,那么最大值变大,极差变小 。这显然是要避免的。
    3.这个区间既包含最大值,又包含最小值,那么最大值和最小值同时变小,且变小幅度一致,极差不变。 这对我们来说是没有影响的。
  • 要最大化极差,显然要尽量进行1操作,不进行2操作,3操作无所谓。所以在选择区间时,只要是包含最小值的,我们都要选择这个区间进行操作。
  • 但是我们不知道最大值,最小值到底在哪里,他们在操作过程中是变化的。比如在样例1中,最开始最大值是第三位3,最后变成了第五位2。
  • 由于最大值最小值的不确定性,我们需要枚举最小值的位置。记录操作过程中,哪个位置作为最终最小值位置时,能够使得极差最大化。
  • 枚举了最小值的位置后,如果暴力进行区间修改操作,其复杂度是很大的,可以过easy version(我的easy version代码就是暴力的)。
精妙之处
  • 我们可以对操作区间进行整理,使得在枚举过程中,不需要重复进行区间操作。
  • 比如我们现在枚举的最小值位置是p,则我们需要选择包含了p的所有区间。
  • 那么p前面一个状态p-1,进行的区间操作是包含了p-1的所有区间,这些p-1区间中,有些是包含了p的,有些是没包含p的,那么我们需要先删除掉没包含p的区间的影响(即把这些区间减掉的1给加回来)。同时还需要补上包含了p但是不包含p-1的区间(这样选择,就不会和p-1里面保留了的区间重复)。
  • 包含了p-1,但不包含p的区间,显然是以p-1结尾的区间。我们用add[]数组保存以r[i]结尾的区间的编号。
  • 包含了p,但不包含p-1的区间,显然是以p开始的区间。我们用sub[]数组保存以l[i]开始的区间的编号。
  • 区间修改操作,就要用到线段树了。
  • 最后输出答案时,使用最终确定的最小值的位置P,包含P的区间留下,不包含P的舍弃。

AC代码

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 305;

int n, m;
int a[maxn], l[maxm], r[maxm], ans[maxn];
vector<int> add[maxn], sub[maxn];

struct node
{
    int l, r;
    int maxx, minn, lazy;
} tree[maxn << 2];

void push_up(int k)
{
    tree[k].maxx = max(tree[k << 1].maxx, tree[k << 1 | 1].maxx);
    tree[k].minn = min(tree[k << 1].minn, tree[k << 1 | 1].minn);
}

void push_down(int k)
{
    if(tree[k].l != tree[k].r)
    {
        tree[k << 1].maxx += tree[k].lazy, tree[k << 1].minn += tree[k].lazy;
        tree[k << 1 | 1].maxx += tree[k].lazy, tree[k << 1 | 1].minn += tree[k].lazy;
        tree[k << 1].lazy += tree[k].lazy;
        tree[k << 1 | 1].lazy += tree[k].lazy;
    }
    tree[k].lazy = 0;
}

void build(int k, int l, int r)
{
    tree[k].l = l;
    tree[k].r = r;
    if(l == r)
    {
        tree[k].maxx = a[l];
        tree[k].minn = a[l];
        tree[k].lazy = 0;
        return ;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid);
    build(k << 1 | 1, mid + 1, r);
    push_up(k);
}

void update(int k, int l, int r, int x)
{
    if(tree[k].lazy)
        push_down(k);
    tree[k].maxx += x;
    tree[k].minn += x;
    if(tree[k].l == l && tree[k].r == r)
    {
        tree[k].lazy += x;
        return ;
    }
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(r <= mid)
        update(k << 1, l, r, x);
    else if(l > mid)
        update(k << 1 | 1, l, r, x);
    else
    {
        update(k << 1, l, mid, x);
        update(k << 1 | 1, mid + 1, r, x);
    }
    push_up(k);
}

int query_max(int k, int l, int r)
{
    int maxx;
    if(tree[k].lazy)
        push_down(k);
    if(tree[k].l == l && tree[k].r == r)
        return tree[k].maxx;
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(r <= mid)
        maxx = query_max(k << 1, l, r);
    else if(l > mid)
        maxx = query_max(k << 1 | 1, l, r);
    else
        maxx = max(query_max(k << 1, l, mid), query_max(k << 1 | 1, mid + 1, r));
    return maxx;
}

int query_min(int k, int l, int r)
{
    int minn;
    if(tree[k].lazy)
        push_down(k);
    if(tree[k].l == l && tree[k].r == r)
        return tree[k].minn;
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(r <= mid)
        minn = query_min(k << 1, l, r);
    else if(l > mid)
        minn = query_min(k << 1 | 1, l, r);
    else
        minn = min(query_min(k << 1, l, mid), query_min(k << 1 | 1, mid + 1, r));
    return minn;
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);

    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d", &l[i], &r[i]);

        //统计区间
        sub[ l[i] ].push_back(i);
        add[ r[i] ].push_back(i);
    }

    //构造线段树
    build(1, 1, n);

    int d = -1, p;//d记录最大极差,p记录最小值的位置
    for(int i = 1; i <= n; i++)//枚举最小值出现的位置
    {
        for(int j = 0; j < add[i - 1].size(); j++)//消除包含i-1,不包含i区间的影响
        {
            int id = add[i - 1][j];//注意这里是i-1
            update(1, l[id], r[id], 1);
        }

        for(int j = 0; j < sub[i].size(); j++)//添加包含i,不包含i-1区间的影响
        {
            int id = sub[i][j];
            update(1, l[id], r[id], -1);
        }

        //查询极差 注意这里虽然知道最小值是a[i],但是由于使用了线段树,有lazy标记,标记可能没有更新到最底层,所以不能直接使用a[i]。
        int det = query_max(1, 1, n) - query_min(1, 1, n);
        if(det > d)
        {
            d = det;
            p = i;
        }
    }

    printf("%d\n", d);
    int t = 0;
    for(int i = 1; i <= m; i++)//统计区间
    {
        if(l[i] <= p && p <= r[i])
            ans[t++] = i;
    }
    printf("%d\n", t);
    for(int i = 0; i < t; i++)
    {
        if(i != 0)
            printf(" ");
        printf("%d", ans[i]);
    }
    printf("\n");
    return 0;
}

### 关于数组启动时有效段不足的问题 在计算机程序运行过程中,&ldquo;not enough valid segments to start array&rdquo; 的错误通常表示分配给数组的有效内存片段不足以支持其初始化操作。这可能由多种原因引起,例如内存碎片化、资源管理不当或者硬件限制等问题。 #### 可能的原因分析 1. **内存分配失败** 如果系统的可用物理内存或虚拟内存不足,则可能导致无法为数组分配足够的连续存储空间[^3]。这种情况下,操作系统可能会返回类似的错误提示。 2. **内存碎片化** 即使总内存容量充足,但如果内存被分割成许多小块而非连续的大块区域,也可能导致无法找到适合大小的连续地址空间来容纳整个数组[^4]。 3. **配置参数设置不合理** 某些编程环境或框架对最大允许使用的堆栈/堆尺寸有严格限制。如果这些限制低于实际需求,就会引发上述问题[^5]。 4. **数据结构设计缺陷** 过大且不必要的单体式静态声明方式容易造成初期加载阶段就耗尽可利用资源的情况发生;动态调整策略可以缓解这一矛盾点所在之处[^6]。 #### 解决方案建议 针对以上提到的各种可能性,这里给出一些通用性的改进建议: - **优化算法逻辑减少占用量** 对目标功能重新审视是否存在冗余计算部分以及能否通过更高效的数据处理手段降低整体消耗水平[^7]。 - **采用分页机制代替一次性装载全部内容** 将原本试图一次读取所有元素改为按需逐步获取子集形式完成任务执行流程转换过程[^8]。 - **增强垃圾回收效率避免长期累积未释放对象占据过多地方** 定期触发清理动作移除不再使用的变量实例从而腾出更多自由位置供后续调用所需使用[^9]。 - **适当增加初始预留额度满足高峰期突发增长的需求场景适应能力提升效果明显可见** 调整相关选项数值向上扩展边界范围直至能够稳定支撑预期规模为止即可达成目的[^10]。 以下是基于 Python 实现的一个简单例子展示如何运用列表推导表达式配合生成器函数实现延迟求值特性以节省瞬态开销成本: ```python def lazy_load_array(size): &quot;&quot;&quot;模拟懒加载行为&quot;&quot;&quot; for i in range(size): yield i * 2 + 1 # 使用生成器替代传统固定长度定义方法 data_stream = lazy_load_array(1_000_000) for value in data_stream: process(value) # 假设这是某个具体业务操作环节 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值