牛客小白月赛86 解题报告 | 珂学家 | 最大子数组和变体 + lazy线段树&动态区间树


前言

在这里插入图片描述


整体评价

终于回归小白月赛的内核了,希望以后也继续保持,_.


A. 水盐平衡

思路: 模拟

题目保证没有浓度相等的情况

盐度 a/b, c/d 的比较关系

演变为 ad, bc 两者的大小关系

#include <bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t-- > 0) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        if (a * d > b * c) {
            cout << "S" << endl;
        } else {
            cout << "Y" << endl;
        }
    }
    return 0;
}

B. 水平考试

思路: 位运算

s, t可以转化为二进制形态

s为t的一个子集,其实表示为

然后 s & t == s

或者 s | t == t

两者皆可

t = int(input())

def mask(s: str) -> int:
    r = 0
    for c in s:
        r |= 1 << (ord(c) - ord('A'))
    return r

for i in range(t):
    s, t = input(), input()
    a, b = mask(s), mask(t)
    if a & b == a:
        print (10)
    else:
        print (0)

C. 数组段数

思路: 分段编码

给每个区间一个递增的编号

然后 id(e) - id(s) + 1

方法二, 利用区间前缀和作差,时间复杂度可以少一个log

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Main {

    public static void main(String[] args) {
        AReader sc = new AReader();
        int n = sc.nextInt(), m = sc.nextInt();

        TreeMap<Integer, Integer> tree = new TreeMap<>();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }

        int segId = 0;
        int j = 0;
        while (j < n) {
            int k = j + 1;
            while (k < n && arr[k - 1] == arr[k]) {
                k++;
            }
            tree.put(j, segId++);
            j = k;
        }

        for (int i =0; i < m; i++) {
            int l = sc.nextInt() - 1, r = sc.nextInt() - 1;
            int res = tree.floorEntry(r).getValue() - tree.floorEntry(l).getValue() + 1;
            System.out.println(res);
        }

    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }

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

//        public BigInteger nextBigInt() {
//            return new BigInteger(next());
//        }
        // 若需要nextDouble等方法,请自行调用Double.parseDouble包装
    }

}

D. 剪纸游戏

思路: 传统的3种方案

  • DFS
  • BFS
  • 并查集

这边借助BFS实现

通过统计矩形的(minX, minY) -> (maxX, maxY)

然后统计 (maxY - minY + 1) * (maxX - minX + 1) == 连通图的个数

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.StringTokenizer;

public class Main {

    static final int[][] dirs = new int[][] {
            {-1, 0}, {1, 0}, {0, -1}, {0, 1}
    };

    static boolean check(int y, int x, char[][] grid, boolean[][] vis) {
        int h = grid.length, w = grid[0].length;

        int minY = y, minX = x;
        int maxY = y, maxX = x;
        int cnt = 1;

        Deque<int[]> deq = new ArrayDeque<>();
        deq.offer(new int[] {y, x});
        vis[y][x] = true;

        while (!deq.isEmpty()) {
            int[] pos = deq.poll();
            for (int i = 0; i < dirs.length; i++) {
                int ty = pos[0] + dirs[i][0];
                int tx = pos[1] + dirs[i][1];
                if (ty >= 0 && ty < h && tx >= 0 && tx < w) {
                    if (grid[ty][tx] == '.' && !vis[ty][tx]) {
                        vis[ty][tx] = true;
                        deq.offer(new int[] {ty, tx});
                        cnt++;

                        minY = Math.min(minY, ty);
                        minX = Math.min(minX, tx);
                        maxY = Math.max(maxY, ty);
                        maxX = Math.max(maxX, tx);
                    }
                }
            }
        }

        return minY == y && minX == x && (maxX - minX + 1) * (maxY - minY + 1) == cnt;
    }

    public static void main(String[] args) {
        AReader sc = new AReader();

        int h = sc.nextInt(), w = sc.nextInt();
        char[][] grids = new char[h][];
        for (int i = 0; i < h; i++) {
            grids[i] = sc.next().toCharArray();
        }

        int res = 0;
        boolean[][] vis = new boolean[h][w];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                if (grids[i][j] == '.' && !vis[i][j]) {
                    if (check(i, j, grids, vis)) {
                        res++;
                    }
                }
            }
        }
        System.out.println(res);

    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }

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

    }

}

E. 可口蛋糕

思路: 双指针滑窗 + 最大子数组和变体

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) {
        AReader sc = new AReader();

        int n = sc.nextInt();
        long W = sc.nextLong();

        long[] wpre = new long[n + 1];
        long[] ws = new long[n];
        for (int i = 0; i < n; i++) {
            ws[i] = sc.nextInt();
            wpre[i + 1] = wpre[i] + ws[i];
        }

        long[] dpre = new long[n + 1];
        long[] ds = new long[n];
        for (int i = 0; i < n; i++) {
            ds[i] = sc.nextInt();
            dpre[i + 1] = dpre[i] + ds[i];
        }

        long res = Long.MIN_VALUE;
        long best = 0;
        int j = 0;

        for (int i = 0; i < n; i++) {
            while (j <= i && (wpre[i + 1] - wpre[j + 1]) >= W) {
                best = Math.max(best + ds[j], ds[j]);
                best = Math.max(0, best);
                j++;
            }

            if (j > 0) {
                res = Math.max(res, best + dpre[i + 1] - dpre[j]);
            }
        }
        System.out.println(res);
    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }

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

}

F. 可口蛋糕

思路: lazy线段树 + 珂朵莉树

lazy线段树用于维护 区间更新+点查询

珂朵莉树,则单纯的维护区间

当然这边用差分数组,可以简化lazy线段树(二阶树状数组)

对于区间更新,可以分为l, r+1的分裂和重新组织

这样的话,其分类讨论的情况就变少了。


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Main {

    public static void main(String[] args) {
        AReader sc = new AReader();

        int n = sc.nextInt(), q = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }

        LazySegTree segTree = new LazySegTree(0, n - 1, arr);

        // 1. 预处理一下
        long acc = 0; // 当前最大的值
        TreeMap<Integer, Integer> tree = new TreeMap<>();
        int j = 0;
        while (j < n) {
            int k = j + 1;
            while (k < n && arr[k - 1] + 1 == arr[k]) {
                k++;
            }
            acc += (long)(k - j) * (k - j);
            tree.put(j, k - 1);
            j = k;
        }

        // 和区间相关的题
        for (int i = 0; i < q; i++) {
            int l = sc.nextInt() - 1, r = sc.nextInt() - 1;
            int d = sc.nextInt();

            if (d == 0) {
                System.out.println(acc);
            } else {
                Map.Entry<Integer, Integer> ent = tree.floorEntry(l);
                segTree.update(l, r, d);
                
                if (ent.getKey() == l) {
                    if (l > 0 && segTree.query(l - 1) + 1 == segTree.query(l)) {
                        Map.Entry<Integer, Integer> ev1 = tree.floorEntry(l - 1);

                        int d1 = ev1.getValue() - ev1.getKey() + 1;
                        int d2 = ent.getValue() - ent.getKey() + 1;
                        acc = acc - (long)d1 * d1 - (long)d2 * d2;
                        acc = acc + (long)(d1 + d2) * (d1 + d2);

                        tree.remove(ev1.getKey());
                        tree.remove(ent.getKey());
                        tree.put(ev1.getKey(), ent.getValue());
                    }
                } else {
                    int d1 = l - ent.getKey();
                    int d2 = ent.getValue() - l + 1;

                    int span = ent.getValue() - ent.getKey() + 1;

                    acc = acc - (long)span * span;
                    acc = acc + (long)d1 * d1 + (long)d2 * d2;

                    tree.remove(ent.getKey());
                    if (ent.getKey() < l) {
                        tree.put(ent.getKey(), l - 1);
                    }
                    tree.put(l, ent.getValue());
                }

                // *)
                Map.Entry<Integer, Integer> ent2 = tree.floorEntry(r);
                if (ent2.getValue() == r) {
                    if (r + 1 < n && segTree.query(r) + 1 == segTree.query(r + 1)) {
                        Map.Entry<Integer, Integer> ev1 = tree.floorEntry(r + 1);

                        int d1 = ent2.getValue() - ent2.getKey() + 1;
                        int d2 = ev1.getValue() - ev1.getKey() + 1;

                        acc = acc - (long)d1 * d1 - (long)d2 * d2;
                        acc = acc + (long)(d1 + d2) * (d1 + d2);

                        tree.remove(ev1.getKey());
                        tree.remove(ent2.getKey());
                        tree.put(ent2.getKey(), ev1.getValue());
                    }
                } else {
                    int d2 = ent2.getValue() - r;
                    int d1 = r - ent2.getKey() + 1;

                    int span = ent2.getValue() - ent2.getKey() + 1;

                    acc = acc - (long)span * span;
                    acc = acc + (long)d1 * d1 + (long)d2 * d2;

                    tree.put(ent2.getKey(), r);
                    if (ent2.getValue() > r) {
                        tree.put(r + 1, ent2.getValue());
                    }
                }
                System.out.println(acc);
            }
        }

    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }

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

//        public BigInteger nextBigInt() {
//            return new BigInteger(next());
//        }
        // 若需要nextDouble等方法,请自行调用Double.parseDouble包装
    }


    static
    public class LazySegTree {

        private int l, r;
        LazySegTree left, right;
        long v;
        long change;

        public LazySegTree(int l, int r, int[] arr) {
            this.l = l;
            this.r = r;
            if (l == r) {
                this.v = arr[l];
            } else {
                int m = l + (r - l) / 2;
                this.left = new LazySegTree(l, m, arr);
                this.right = new LazySegTree(m + 1, r, arr);
            }
        }

        public void update(int ul, int ur, int delta) {
            if (isLeaf()) {
                this.change += delta;
                this.v += this.change;
                this.change = 0;
                return;
            }
            if (this.l >= ul && this.r <= ur) {
                this.change += delta;
                return;
            }
            pushDown();

            int m = l + (r - l) / 2;
            if (ur <= m) {
                this.left.update(ul, ur, delta);
            } else if (ul > m) {
                this.right.update(ul, ur, delta);
            } else {
                this.left.update(ul, m, delta);
                this.right.update(m + 1, ur, delta);
            }
            pushUp();
        }

        public long query(int p) {
            if (isLeaf()) {
                if (this.change != 0) {
                    this.v += this.change;
                    this.change = 0;
                }
                return this.v;
            }
            pushDown();
            int m = l + (r - l) / 2;
            if (p <= m) {
                return this.left.query(p);
            } else {
                return this.right.query(p);
            }
        }

        void pushDown() {
            if (this.change != 0) {
                if (this.left != null) this.left.change += this.change;
                if (this.right != null) this.right.change += this.change;
                this.change = 0;
            }
        }

        void pushUp() {
        }

        boolean isLeaf() {
            return this.l == this.r;
        }

    }

}

写在最后

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值