Educational Codeforces Round 152 (Rated for Div. 2) ----- E - Max to the Right of Min ----- 题解

本文介绍了如何解决一个编程问题,即在一个给定数组中找到最小值索引小于最大值索引的子数组数量。通过分析题意,提出了一种高效的解决方案,涉及枚举、索引扩展和数据结构优化,最终给出了Java代码实现。
摘要由CSDN通过智能技术生成

目录

 E - Max to the Right of Min:

题目大意:

思路解析:

代码实现:


 E - Max to the Right of Min:

题目大意:

思路解析:

其实这道题的题意大概就是,现在有一个数组,问这个数组的子数组中满足最小值的索引小于最大值索引的子数组个数。

那我们想是否可以通过枚举其中一个端点,来确定其他端点是否可能。

例如我们是否能构造这样的情况   [l, .... i, .... r]  在这个数组中ai的值最大,a[r] > a[l], 并且 l+1 ---- r-1这些索引的值都大于a[r],那么现在的最小值索引为l, 如果往后考虑 只要不出现一个索引 x,满足(x<l, 并且a[x] > a[i]) 那么这样的子数组都是合法的,换言之 l最多扩展到x,那我们只要预处理得到左边数字中第一个大于ai的索引值,那我们就可以快速得到以r为右端点的所有答案,

那我们可考虑这个r是否进行下一步扩展,假设a[r+1] 大于a[r],那么l‘ 可能是大于 l 的, 那我们答案是否是对于l’进行扩展呢?答案是否定的,因为a[l'] 是大于 a[r] 的,那么对于 [l' ..... i ...... r+1] 来说最小值的索引为r,此时最大值索引为i,不是合法数组了,那么扩展还是应当用l来进行扩展。     假设a[r+1] 小于 a[r], 那么l' 一定小于等于 l的那我们利用l'进行扩展,可以保证数组合法了。直到a[r'] > a[i] 时,停止计算答案。因为我们的统计建立在 a[i]为子数组的最大值的情况下,这样可以保证重复计算。

通过上述分析我们需要知道  每个位置i左边小于它的第一个数字的索引, 每个位置i左边大于它的第一个数字的索引, 每个位置i右边大于它的第一个数字的索引

那么如果   [l, i ,r]   l为左边的第一个大于a[i]的索引 r 为右边的第一个大于a[i]的索引,第一种情况是遍历【i,r] 但是如果这部分区间很长,但是左部分区间很短,那么遍历右半区间明显是不利的,那我们想是否可以通过确定左端点来确定答案。

假设 [l ....... i ........ r] 其中 a[i] 为当前数组中最大的那个, a[l] > a[r], 除了r其他值都大于a[l],可以发现这个情况其实和第一种情况是类似的,最小值的索引为 r, 但是现在索引是大于i的,那我们只要选择 [i,r-1] 作为右端点,那么这个 最小值索引就为l ,为合法的。那么考虑是否可以进行扩展,如果a[l-1] > a[l] 那么 r' 可能小于r, 但是我们只要选择的右端点为[i,r-1]还是能满足数组合法,所以还是利用r作为右端点。如果a[l-1] < a[l], 那么r‘ 一定大于等于r,此时我们只要选择 [i,r'-1]时,l-1就是最小值索引,那么可以利用r’作为右端点,但是可能r' 大于了 rm (rm为右边第一个大于a[i]的索引)虽然我们选择 [rm, r‘-1] 最小值索引为l-1也能满足合法,但是此时a[i]不是作为子数组的最大值,应该排除(这部分答案,只保留 [i,rm-1]) 。那么显然a[l-1]也不能大于a[i]

通过上述分析我们需要知道  每个位置i左边小于它的第一个数字的索引, 每个位置i左边大于它的第一个数字的索引, 每个位置i右边大于它的第一个数字的索引,每个位置i右边小于它的第一个数字的索引。

代码实现:


import java.util.*;


import java.io.*;

public class Main {

    public static void main(String[] args) throws IOException {

        int t = 1;
        while (t > 0) {
            solve();
            t--;
        }
        w.flush();
        w.close();
    }

    static int mod = 998244353;
    static long inf = (long) 1e18;


    static long ans = 0;
    public static void solve() throws IOException {
        int n = f.nextInt();
        int[] a = new int[n+1];
        int[] lmi = new int[n+1];
        int[] rmi = new int[n+1];
        int[] lma = new int[n+1];
        int[] rma = new int[n+1];
        for (int i = 1; i <= n; i++) {
            a[i] = f.nextInt();
        }
        int tp = 0;int[] st = new int[1000005];
        tp = 0; st[0] = 0;
        for(int i = 1; i <= n; ++i){
            while(tp > 0 && a[i] < a[st[tp]]) --tp;
            lmi[i] = st[tp];
            st[++tp] = i;
        }
        tp = 0; st[0] = 0;
        for(int i = 1; i <= n; ++i){
            while(tp > 0&& a[i] > a[st[tp]]) --tp;
            lma[i] = st[tp];
            st[++tp] = i;
        }
        tp = 0; st[0] = n + 1;
        for(int i = n; i >= 1; --i){
            while(tp > 0 && a[i] < a[st[tp]]) --tp;
            rmi[i] = st[tp];
            st[++tp] = i;
        }
        tp = 0; st[0] = n + 1;
        for(int i = n; i >= 1; --i){
            while(tp > 0 && a[i] > a[st[tp]]) --tp;
            rma[i] = st[tp];
            st[++tp] = i;
        }
        for (int i = 1; i <= n; i++) {
            int lm = lma[i]; int rm = rma[i];
            if (i - lm >= rm - i){
                int cur = n;
                for (int j = i; j < rm; j++) {
                    cur = Math.min(cur, lmi[j]);
                    ans += Math.max(0, cur - lm);
                }
            }else {
                int cur = i;
                for (int j = i; j > lm; --j) {
                    if(a[j] < a[cur]) cur = j;
                    if(cur != i){
                        ans += Math.max(0, Math.min(rmi[cur], rm) - i);
                    }
                }
            }
        }
        w.println(ans);
    }



    static int gcd(int a, int b) {
        if (a == 0) return b;
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    public static int qkm(int a, int b) {
        int res = 1;
        while (b > 0) {
            if ((b & 1) == 1) res *= a;
            a *= a;
            b >>= 1;
        }
        return res;
    }

    public static void swap(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }


    public static long qkm(long a, long b, long mod) {
        long res = 1;
        while (b > 0) {
            if ((b & 1) == 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }

    public static class Node {
        int a;
        int b;

        public Node(int x, int val) {
            this.a = x;

            this.b = val;
        }
        public Node(){}
    }


    static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
    static Input f = new Input(System.in);

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() throws IOException {

            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                tokenizer = new StringTokenizer(reader.readLine());
            }
            return tokenizer.nextToken();
        }

        public String nextLine() throws IOException {
            String str = null;
            str = reader.readLine();
            return str;
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public Double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值