Codeforces 1108E2 Array and Segments (Hard version) 差分, 暴力

Codeforces 1108E2

E2. Array and Segments (Hard version)

Description:

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 \(a_i\).

You are also given a set of \(m\) segments. The \(j\)-th segment is \([l_j; r_j]\), where \(1 \le l_j \le r_j \le 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 \(\max\limits_{i=1}^{n}b_i - \min\limits_{i=1}^{n}b_i\) 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 \le n \le 10^5, 0 \le m \le 300\)) — the length of the array \(a\) and the number of segments, respectively.

The second line of the input contains \(n\) integers \(a_1, a_2, \dots, a_n\) (\(-10^6 \le a_i \le 10^6\)), where \(a_i\) 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 \(l_j\) and \(r_j\) (\(1 \le l_j \le r_j \le n\)), where \(l_j\) and \(r_j\) are the ends of the \(j\)-th segment.

Output

In the first line of the output print one integer \(d\) — the maximum possible value \(\max\limits_{i=1}^{n}b_i - \min\limits_{i=1}^{n}b_i\) 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 \le q \le m\)) — the number of segments you apply.

In the third line print \(q\) distinct integers \(c_1, c_2, \dots, c_q\) in any order (\(1 \le c_k \le m\)) — indices of segments you apply to the array \(a\) in such a way that the value \(\max\limits_{i=1}^{n}b_i - \min\limits_{i=1}^{n}b_i\) of the obtained array \(b\) is maximum possible.

If there are multiple answers, you can print any.

Sample Input:

5 4

2 -2 3 1 2

1 3

4 5

2 5

1 3

Sample Output:

6

2

4 1

Sample Input:

5 4

2 -2 3 1 4

3 5

3 4

2 4

2 5

Sample Output:

7

2

3 2

Sample Input:

1 0

1000000

Sample Output:

0

0

题目链接

题解:

有一个长为\(n\)的数列,有\(m\)个线段,每个线段将该线段区间的所有数减一,你可以选任意个线段,要求最大化极差并输出一种方案

这种极差的题一个套路是固定最大值求最小值

那么我们可以枚举每一个数作为最大值的方案,对不包含这个数的线段进行操作,然后找最大最小值即可,利用差分的思想单次操作可以\(O(1)\),最后查询极值\(O(n)\),这样我们就找到了一个\(O(n^2)\)的优秀算法,可以通过这题的简单版本

然后我们注意到线段数很少,只有\(300\)个,那么我们可以将原数列分为至多\(600\)段,每一段的数作为最大值时策略是相同的,我们就的到了\(O(n \cdot m +m^2)\)的算法,cf机子上跑得飞快

另外,可以用线段树加速操作得到\(O(mlog(n))\)的做法

甚至可以将\(n\)也变成\(m\),因为我们只关心每一段的极值,可以把原数列切成至多\(600\)段,每一段记录最大最小值即可,复杂度为\(O(m^2)\), 不知道为什么评论指出这个算法的老哥的代码跑的还没我\(O(n \cdot m + m^2)\)快...

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, M = 310;

int n, a[N], b[N], ans, l[M], r[M], m, rec, cnt;
set<int> key;

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]);
        key.insert(l[i]);
        key.insert(r[i] + 1);
    }
    ans = *max_element(a + 1, a + n + 1) - *min_element(a + 1, a + n + 1);
    for(auto it = key.begin(); it != key.end(); ++it) {
        int i = *it; ++cnt;
        memset(b, 0, sizeof(b));
        int mx = -1e9, mn = 1e9, sum = 0;
        for(int j = 1; j <= m; ++j) {
            if(l[j] <= i && i <= r[j]) continue;
            b[l[j]]--, b[r[j] + 1]++;
        }
        for(int j = 1; j <= n; ++j) {
            sum += b[j];
            mx = max(mx, a[j] + sum);
            mn = min(mn, a[j] + sum);
        }
        if(mx - mn > ans) {
            rec = i;
            ans = mx - mn;
        }
    }
    printf("%d\n", ans);
    if(rec) {
        vector<int> res;
        for(int i = 1; i <= m; ++i) {
            if(l[i] <= rec && rec <= r[i]) continue;
            res.push_back(i);
        }
        printf("%d\n", (int)res.size());
        for(int i = 0; i < res.size(); ++i)
            printf("%d%c", res[i], " \n"[i == res.size() - 1]);
    }
    else
        puts("0\n");
    return 0;
}

原文地址:https://www.cnblogs.com/tusikalanse/p/10349363.html

时间: 2024-10-08 20:29:21

Codeforces 1108E2 Array and Segments (Hard version) 差分, 暴力的相关文章

CF #535 (Div. 3) E2 Array and Segments (Hard version) 利用线段树进行区间转移

传送门 题意:    有m个区间,n个a[ i ] , 选择若干个区间,使得整个数组中的最大值和最小值的差值最小.n<=1e5,m<=300; 思路: 可以知道每个i,如果一个区间包含这个点,就让这个区间发挥作用.枚举每个i,找到最大值即可. 当然这个复杂度不对,我们可以通过线段树保存数组的最大值和最小值,每次区间在左端点发挥作用,在右端点去掉作用. #include <algorithm> #include <iterator> #include <iostre

CF1108E1 Array and Segments (Easy version)(待更新)

题目地址:CF1108E1 加强版题解:https://www.cnblogs.com/xht37/p/10322344.html 原文地址:https://www.cnblogs.com/xht37/p/10322340.html

codeforces A. Array题解

Vitaly has an array of n distinct integers. Vitaly wants to divide this array into three non-empty sets so as the following conditions hold: The product of all numbers in the first set is less than zero (?<?0). The product of all numbers in the secon

Codeforces 57C Array dp暴力找规律

题目链接:点击打开链接 先是计算非递增的方案, 若非递增的方案数为x, 则非递减的方案数也是x 答案就是 2*x - n 只需求得x即可. 可以先写个n3的dp,然后发现规律是 C(n-1, 2*n-1) 然后套个逆元即可. #include<iostream> #include<cstdio> #include<vector> #include<string.h> using namespace std; #define ll long long #def

Codeforces 430A Points and Segments (easy)

题意:让你染色点,要求在给出的区间内|红色个数-蓝色个数|<=1 思路:排序后依次交替染色就能达到效果 #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int MAXN = 110; int arr[MAXN]; int n,m,x,y; int

Codeforces 1335E2 - Three Blocks Palindrome (hard version)

题面 题意/解题思路 直接延用 Easy 版本的想法即可 详解见上一篇博客Codeforces 1335E1 - Three Blocks Palindrome (easy version) 完整程序 (93ms/2000ms) #include<bits/stdc++.h> using namespace std; int ar[200050]; vector<int> v[210]; void solve() { int n,ans=0; cin>>n; for(i

Codeforces 505 A Mr. Kitayuta&#39;s Gift【暴力】

题意:给出一个字符串,可以向该字符串的任意位置插入一个字母使其变成回文串 因为字符串的长度小于10,枚举插入的字母,和要插入的位置,再判断是否已经满足回文串 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map> 8

Codeforces Round #359 (Div. 2) C. Robbers&#39; watch (暴力DFS)

题目链接:http://codeforces.com/problemset/problem/686/C 给你n和m,问你有多少对(a, b) 满足0<=a <n 且 0 <=b < m 且a的7进制和n-1的7进制位数相同 且b的7进制和m-1的7进制位数相同,还有a和b的7进制上的每位上的数各不相同. 看懂题目,就很简单了,先判断a和b的7进制位数是否超过7,不超过的话就dfs暴力枚举计算就可以了. 1 //#pragma comment(linker, "/STACK

CodeForces 686B - Little Robber Girl&#39;s Zoo(暴力)

题意:将一个n(1 <= n <= 100)个元素的序列排成非递减序列,每次操作可以指定区间[ L,R ](区间内元素个数为偶数),将区间内第一项与第二项交换,第三项与第四项交换,第五项与第六项--在2W次内完成排序,输出每次操作. 瞎搞即可,不断检查相邻元素是否满足 前者>=后者,不满足即交换,直到序列满足条件位置(n最大为100绝对不会超过2W步) #include<cstdio> #include<cstring> #include<cctype>