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

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

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

You are given an array aa consisting of nn integers. The value of the ii-th element of the array is aiai.

You are also given a set of mm segments. The jj-th segment is [lj;rj][lj;rj], where 1≤lj≤rj≤n1≤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]a=[0,0,0,0,0] and the given segments are [1;3][1;3] and [2;4][2;4] then you can choose both of them and the array will become b=[−1,−2,−2,−1,0]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 aa and obtain the array bb then the value maxi=1nbi−mini=1nbimaxi=1nbi−mini=1nbi 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 nn and mm (1≤n≤105,0≤m≤3001≤n≤105,0≤m≤300) — the length of the array aa and the number of segments, respectively.

The second line of the input contains nn integers a1,a2,…,ana1,a2,…,an (−106≤ai≤106−106≤ai≤106), where aiai is the value of the ii-th element of the array aa.

The next mm lines are contain two integers each. The jj-th of them contains two integers ljlj and rjrj (1≤lj≤rj≤n1≤lj≤rj≤n), where ljlj and rjrj are the ends of the jj-th segment.

Output

In the first line of the output print one integer dd — the maximum possible value maxi=1nbi−mini=1nbimaxi=1nbi−mini=1nbi if bb is the array obtained by applying some subset of the given segments to the array aa.

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

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

If there are multiple answers, you can print any.

题目链接:http://codeforces.com/contest/1108/problem/E2

题目大意:

给你n个数,m段区间,你可以选择任意段区间并对区间内的数-1.现在你需要任意段区间使得n个数里的最大值-最小值的差(极差)最大.

思路: 

发现对一段区间进行-1操作并不会影响这段区间内的极差,可以得知n个数里的最大值和最小值不可能同时减小, 若要使极差最大化

必然存在一个最大值的数没有进行减小操作.我们可以枚举最大值,然后把所有不包含这个最大值的区间都进行减小,便可以求出最终答案.

 

easy version的范围是(1≤n≤300,0≤m≤300) 简单版本数据范围较小,可以直接暴力,我是用差分数组(有兴趣自行百度)写的O(n*(n+m)).

hard version的范围是(1≤n≤1e5,0≤m≤300) 这个稍微复杂点,但不难想到用线段树优化,这样查找最小值的复杂度变成logn,最后便只要O(n*m*logn).

不过这题还是蛮坑的,如果线段树暴力更新每一个区间是会超时的,你还得优化下.比如我现在要更新这点i,然后枚举区间[l,r],如果我现在这个ii-1(i前面的一个点)都在[l,r]区间内的话,那我就可以跳过此次更新,其它情况类似.

以下是代码:

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct Node
{
    int val,lazy;
}tree[N<<2];
int a[N],l[N],r[N];

inline void build_tree(int L,int R,int k)
{
    tree[k].lazy=0;
    if(L==R)
    {
        tree[k].val=a[L];
        return ;
    }
    int m=L+(R-L)/2;
    build_tree(L,m,k<<1);
    build_tree(m+1,R,k<<1|1);
    tree[k].val=min(tree[k<<1].val,tree[k<<1|1].val);
}
inline void push_down(int l,int r,int k)
{
    if(l!=r)
    {
        tree[k<<1].val+=tree[k].lazy;tree[k<<1].lazy+=tree[k].lazy;
        tree[k<<1|1].val+=tree[k].lazy;tree[k<<1|1].lazy+=tree[k].lazy;
    }
    tree[k].lazy=0;
}
inline void update(int L,int R,int l,int r,int k,int val)
{
    if(l<=L&&r>=R)
    {
        tree[k].val+=val;
        tree[k].lazy+=val;
        return ;
    }
    if(tree[k].lazy)
    {
        push_down(L,R,k);
    }
    
    int m=L+(R-L)/2;
    if(l<=m) update(L,m,l,r,k<<1,val);
    if(r> m) update(m+1,R,l,r,k<<1|1,val);
    tree[k].val=min(tree[k<<1].val,tree[k<<1|1].val);
}
vector<int> vc;
int main()
{
    int n,m;
    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]);
    int ans=-1,flag;//flag是最大值位置
    build_tree(1,n,1);
    for(int i=1;i<=m;i++)
        update(1,n,l[i],r[i],1,-1);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            //分别是 i和i-1 在[l,r]区间内的情况.
            if(l[j]<=i&&r[j]>=i&&l[j]<=i-1&&r[j]>=i-1) continue;
            if((l[j]<=i&&r[j]>=i)==0&&l[j]<=i-1&&r[j]>=i-1)
            update(1,n,l[j],r[j],1,-1);
            else if(l[j]<=i&&r[j]>=i) update(1,n,l[j],r[j],1,1);
        }
        int mn=tree[1].val;//因为查询区间是[1,n],所以就直接是第一个了
        if(ans<a[i]-mn)
        {
            flag=i;
            ans=a[i]-mn;
        }
    }
    printf("%d\n",ans);
    for(int i=1;i<=m;i++)
    {
        if(l[i]<=flag&&r[i]>=flag) continue;
        vc.push_back(i);
    }
    printf("%d\n",vc.size());
    for(int i=0;i<vc.size();i++)
        printf("%d ",vc[i]);

    return 0;
}

线段树好容易写错啊,orz

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值