力扣第 159 场双周赛
2025年7月1日大约 6 分钟
力扣第 159 场双周赛
比赛时间 2025-06-21。本场周赛国服共 32 人 AK。
《紫罗兰永恒花园》
果然“成长”是一个
具有普世性的主题
所以对 30 岁 40 岁的
男观众和女观众来说
是非常受欢迎的主题—— ABC Animation 首席执行官:西出将之
T1. 最小相邻交换至奇偶交替(4 分)
解题思路
统计奇数和偶数的频次,再分类讨论。
时间复杂度:O(n)。
参考代码
class Solution {
    public int minSwaps(int[] nums) {
        int[] cnt = new int[2];
        for (int v : nums) cnt[v % 2]++;
        if (Math.abs(cnt[0] - cnt[1]) > 1) return -1;
        if (cnt[0] == cnt[1]) {
            return Math.min(getAns(nums, 0), getAns(nums, 1));
        } else if (cnt[0] > cnt[1]) { // 第一个偶数
            return getAns(nums, 0);
        } else {
            return getAns(nums, 1);
        }
    }
    // st:0 偶数开头 st:1 奇数开头
    private int getAns(int[] nums, int st) {
        int ans = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] % 2 == 0) {
                ans += Math.abs(i - st);
                st += 2;
            }
        }
        return ans;
    }
}T2. 找到最大三角形面积(4 分)
解题思路
枚举平行于 x 轴情况:底边长度 为 maxXInY - minXInY,高度为 max(y - minY, maxY - y)。平行于 y 轴情况同理。
时间复杂度:O(n)。
参考代码
class Solution {
    public long maxArea(int[][] coords) {
        return Math.max(getAns(coords, 0), getAns(coords, 1));
    }
    // xy_type:0=x 1=y
    private long getAns(int[][] coords, int xy_type) {
        long minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE;
        Map<Integer, Integer> minXForY = new HashMap<>();
        Map<Integer, Integer> maxXForY = new HashMap<>();
        for (int[] p : coords) {
            int x = p[xy_type], y = p[1 - xy_type];
            if (y < minY) minY = y;
            if (y > maxY) maxY = y;
            minXForY.merge(y, x, Math::min);
            maxXForY.merge(y, x, Math::max);
        }
        long ans = -1;
        // 处理平行于x轴的边的情况
        for (Map.Entry<Integer, Integer> entry : minXForY.entrySet()) {
            int y = entry.getKey();
            int minXInY = entry.getValue();
            int maxXInY = maxXForY.get(y);
            long base = maxXInY - minXInY;
            if (base <= 0) continue; // 底边长度无效
            long height = Math.max(y - minY, maxY - y);
            if (height <= 0) continue; // 高无效
            ans = Math.max(ans, base * height);
        }
        return ans;
    }
}T3. 计数质数间隔平衡子数组(5 分)
解题思路
预处理质数 + 滑动窗口最大值最小值。
时间复杂度:O(n)。
参考代码
class Solution {
    static final int MAX_N = (int) 1e5;
    static boolean[] np;
    static {
        np = new boolean[MAX_N + 1];
        np[1] = true;
        for (int i = 2; i * i <= MAX_N; i++) {
            if (!np[i]) {
                for (int j = i * i; j <= MAX_N; j += i) {
                    np[j] = true;
                }
            }
        }
    }
    public int primeSubarray(int[] nums, int k) {
        int n = nums.length, l = 0, r = 0, ans = 0;
        Deque<Integer> maxQ = new ArrayDeque<>(); // maxQ.getFirst() 为区间内最大值下标
        Deque<Integer> minQ = new ArrayDeque<>(); // minQ.getFirst() 为区间内最小值下标
        // 上上个质数位置 last2
        int last = -1, last2 = -1;
        while (r < n) {
            if (!np[nums[r]]) {
                // 1、入
                last2 = last;
                last = r;
                // 这里加不加 = 都可以
                while (!maxQ.isEmpty() && nums[r] > nums[maxQ.getLast()]) maxQ.removeLast();
                maxQ.addLast(r);
                while (!minQ.isEmpty() && nums[r] < nums[minQ.getLast()]) minQ.removeLast();
                minQ.addLast(r);
                while (!maxQ.isEmpty() && !minQ.isEmpty()
                        && nums[maxQ.getFirst()] - nums[minQ.getFirst()] > k) {
                    if (maxQ.getFirst() <= l) maxQ.removeFirst();
                    if (minQ.getFirst() <= l) minQ.removeFirst();
                    l++;
                }
            }
            // 3、更新答案。当右端点固定时,左端点合法范围 [l, last2]
            ans += last2 - l + 1;
            r++;
        }
        return ans;
    }
}T4. 第 K 小的路径异或和(6 分)
解题思路 & 参考代码
离散化 + 树上启发式合并 + 树状数组。
时间复杂度:O(n log^2 n)。
class Solution {
    List<Integer>[] g;
    int[] vals;
    Map<Integer, List<int[]>> queryMap;
    Map<Integer, Integer> map;
    BIT tr;
    int[] xor, sz, heavy, distinct, cntArr, ans;
    public int[] kthSmallest(int[] par, int[] vals, int[][] queries) {
        int n = vals.length;
        int q = queries.length;
        g = new ArrayList[n];
        Arrays.setAll(g, e -> new ArrayList<>());
        for (int i = 1; i < n; i++) {
            g[par[i]].add(i);
        }
        this.vals = vals;
        xor = new int[n];
        sz = new int[n];
        heavy = new int[n];
        Arrays.fill(heavy, -1);
        calcXor(0, 0);
        dfs2(0);
        queryMap = new HashMap<>();
        for (int i = 0; i < q; i++) {
            int u = queries[i][0];
            int kj = queries[i][1];
            queryMap.computeIfAbsent(u, k -> new ArrayList<>()).add(new int[]{kj, i});
        }
        int[] allXor = Arrays.copyOf(xor, n);
        Arrays.sort(allXor);
        int m = 0;
        for (int i = 1; i < n; i++) {
            if (allXor[i] != allXor[m]) {
                allXor[++m] = allXor[i];
            }
        }
        distinct = Arrays.copyOf(allXor, m + 1);
        map = new HashMap<>();
        for (int i = 0; i <= m; i++) {
            map.put(distinct[i], i + 1);
        }
        int M = m + 1;
        tr = new BIT(M);
        cntArr = new int[M + 1];
        ans = new int[q];
        dfs(0, true);
        return ans;
    }
    private void calcXor(int u, int prevXor) {
        xor[u] = prevXor ^ vals[u];
        for (int v : g[u]) {
            calcXor(v, xor[u]);
        }
    }
    private void dfs2(int u) {
        sz[u] = 1;
        int maxSize = 0;
        for (int v : g[u]) {
            dfs2(v);
            sz[u] += sz[v];
            if (sz[v] > maxSize) {
                maxSize = sz[v];
                heavy[u] = v;
            }
        }
    }
    private void add(int u) {
        int x = xor[u];
        int id = map.get(x);
        if (cntArr[id] == 0) {
            tr.add(id, 1);
        }
        cntArr[id]++;
    }
    private void remove(int u) {
        int x = xor[u];
        int id = map.get(x);
        cntArr[id]--;
        if (cntArr[id] == 0) {
            tr.add(id, -1);
        }
    }
    private void addTree(int u) {
        Deque<int[]> st = new ArrayDeque<>();
        st.push(new int[]{u, 0});
        while (!st.isEmpty()) {
            int[] curInfo = st.pop();
            int node = curInfo[0];
            int idx = curInfo[1];
            if (idx == 0) {
                add(node);
            }
            if (idx < g[node].size()) {
                int next = g[node].get(idx);
                st.push(new int[]{node, idx + 1});
                st.push(new int[]{next, 0});
            }
        }
    }
    private void removeTree(int u) {
        Deque<int[]> st = new ArrayDeque<>();
        st.push(new int[]{u, 0});
        while (!st.isEmpty()) {
            int[] curInfo = st.pop();
            int node = curInfo[0];
            int idx = curInfo[1];
            if (idx == 0) {
                remove(node);
            }
            if (idx < g[node].size()) {
                int next = g[node].get(idx);
                st.push(new int[]{node, idx + 1});
                st.push(new int[]{next, 0});
            }
        }
    }
    private void dfs(int u, boolean keep) {
        for (int v : g[u]) {
            if (v == heavy[u]) continue;
            dfs(v, false);
        }
        if (heavy[u] != -1) {
            dfs(heavy[u], true);
        }
        add(u);
        for (int v : g[u]) {
            if (v == heavy[u]) continue;
            addTree(v);
        }
        if (queryMap.containsKey(u)) {
            for (int[] q : queryMap.get(u)) {
                int kj = q[0];
                int idx = q[1];
                int total = tr.query(tr.n);
                if (total < kj) {
                    ans[idx] = -1;
                } else {
                    int left = 1, right = tr.n;
                    while (left < right) {
                        int mid = left + (right - left) / 2;
                        if (tr.query(mid) >= kj) {
                            right = mid;
                        } else {
                            left = mid + 1;
                        }
                    }
                    ans[idx] = distinct[left - 1];
                }
            }
        }
        if (!keep) {
            removeTree(u);
        }
    }
    static class BIT {
        int n;
        int[] tree;
        public BIT(int n) {
            this.n = n;
            tree = new int[n + 1];
        }
        int lb(int x) {
            return x & -x;
        }
        void add(int pos, int val) {
            for (; pos <= n; pos += lb(pos)) tree[pos] += val;
        }
        int query(int pos) {
            int ret = 0;
            for (; pos > 0; pos -= lb(pos)) ret += tree[pos];
            return ret;
        }
    }
}离散化 + 树上启发式合并 + 线段树上二分。
class Solution {
    List<Integer>[] g;
    int[] vals;
    int[] xor, size, son, ans;
    List<int[]>[] queryGroup;
    static class Node {
        Node l, r;
        int sum;
    }
    public int[] kthSmallest(int[] par, int[] vals, int[][] queries) {
        int n = vals.length;
        int q = queries.length;
        g = new ArrayList[n];
        Arrays.setAll(g, e -> new ArrayList<>());
        for (int i = 1; i < n; i++) {
            g[par[i]].add(i);
        }
        this.vals = vals;
        xor = new int[n];
        calcXor(0, 0);
        List<Integer> allValsList = new ArrayList<>();
        for (int x : xor) {
            allValsList.add(x);
        }
        Collections.sort(allValsList);
        Map<Integer, Integer> map = new HashMap<>();
        int idx = 0;
        List<Integer> distinctVals = new ArrayList<>();
        for (int i = 0; i < allValsList.size(); i++) {
            if (i == 0 || !allValsList.get(i).equals(allValsList.get(i - 1))) {
                int x = allValsList.get(i);
                map.put(x, idx);
                distinctVals.add(x);
                idx++;
            }
        }
        int valSize = distinctVals.size();
        size = new int[n];
        son = new int[n];
        Arrays.fill(son, -1);
        dfs1(0);
        queryGroup = new ArrayList[n];
        Arrays.setAll(queryGroup, e -> new ArrayList<>());
        for (int i = 0; i < q; i++) {
            int u = queries[i][0];
            int k = queries[i][1];
            queryGroup[u].add(new int[]{i, k});
        }
        ans = new int[q];
        dfs2(0, true, map, distinctVals, valSize);
        return ans;
    }
    private void calcXor(int u, int prevXor) {
        xor[u] = prevXor ^ vals[u];
        for (int v : g[u]) {
            calcXor(v, xor[u]);
        }
    }
    void dfs1(int u) {
        size[u] = 1;
        for (int v : g[u]) {
            dfs1(v);
            size[u] += size[v];
            if (son[u] == -1 || size[v] > size[son[u]]) {
                son[u] = v;
            }
        }
    }
    Node dfs2(int u, boolean keep, Map<Integer, Integer> map, List<Integer> distinctVals, int valSize) {
        Node root = null;
        if (son[u] != -1) {
            root = dfs2(son[u], true, map, distinctVals, valSize);
        }
        for (int v : g[u]) {
            if (v == son[u]) continue;
            Node childTree = dfs2(v, false, map, distinctVals, valSize);
            root = merge(root, childTree, 0, valSize - 1);
        }
        int x = map.get(xor[u]);
        root = insert(root, 0, valSize - 1, x);
        for (int[] q : queryGroup[u]) {
            int qIdx = q[0];
            int k = q[1];
            if (root == null || root.sum < k) {
                ans[qIdx] = -1;
            } else {
                int pos = query(root, 0, valSize - 1, k);
                ans[qIdx] = distinctVals.get(pos);
            }
        }
        if (!keep) {
            return root;
        }
        return root;
    }
    Node merge(Node a, Node b, int l, int r) {
        if (a == null) return b;
        if (b == null) return a;
        if (l == r) {
            a.sum = 1;
            return a;
        }
        int mid = (l + r) >>> 1;
        a.l = merge(a.l, b.l, l, mid);
        a.r = merge(a.r, b.r, mid + 1, r);
        a.sum = (a.l != null ? a.l.sum : 0) + (a.r != null ? a.r.sum : 0);
        return a;
    }
    Node insert(Node root, int l, int r, int x) {
        if (root == null) {
            root = new Node();
        }
        if (l == r) {
            root.sum = 1;
            return root;
        }
        int mid = (l + r) >>> 1;
        if (x <= mid) {
            root.l = insert(root.l, l, mid, x);
        } else {
            root.r = insert(root.r, mid + 1, r, x);
        }
        root.sum = (root.l != null ? root.l.sum : 0) + (root.r != null ? root.r.sum : 0);
        return root;
    }
    int query(Node root, int l, int r, int k) {
        if (l == r) {
            return l;
        }
        int mid = (l + r) >>> 1;
        int leftSum = (root.l != null) ? root.l.sum : 0;
        if (leftSum >= k) {
            return query(root.l, l, mid, k);
        } else {
            return query(root.r, mid + 1, r, k - leftSum);
        }
    }
}(全文完)