【線段樹】1108E2 Array and Segments (Hard version) & 1108E1 Array and Segments (Easy version)

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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章