跳转至

3161. 物块放置查询

题目描述

有一条无限长的数轴,原点在 0 处,沿着 x 轴  方向无限延伸。

给你一个二维数组 queries ,它包含两种操作:

  1. 操作类型 1 :queries[i] = [1, x] 。在距离原点 x 处建一个障碍物。数据保证当操作执行的时候,位置 x 处 没有 任何障碍物。
  2. 操作类型 2 :queries[i] = [2, x, sz] 。判断在数轴范围 [0, x] 内是否可以放置一个长度为 sz 的物块,这个物块需要 完全 放置在范围 [0, x] 内。如果物块与任何障碍物有重合,那么这个物块 不能 被放置,但物块可以与障碍物刚好接触。注意,你只是进行查询,并 不是 真的放置这个物块。每个查询都是相互独立的。

请你返回一个 boolean 数组results ,如果第 i 个操作类型 2 的操作你可以放置物块,那么 results[i] 为 true ,否则为 false 。

 

示例 1:

输入:queries = [[1,2],[2,3,3],[2,3,1],[2,2,2]]

输出:[false,true,true]

解释:

查询 0 ,在 x = 2 处放置一个障碍物。在 x = 3 之前任何大小不超过 2 的物块都可以被放置。

示例 2:

输入:queries = [[1,7],[2,7,6],[1,2],[2,7,5],[2,7,6]]

输出:[true,true,false]

解释:

  • 查询 0 在 x = 7 处放置一个障碍物。在 x = 7 之前任何大小不超过 7 的物块都可以被放置。
  • 查询 2 在 x = 2 处放置一个障碍物。现在,在 x = 7 之前任何大小不超过 5 的物块可以被放置,x = 2 之前任何大小不超过 2 的物块可以被放置。

 

提示:

  • 1 <= queries.length <= 15 * 104
  • 2 <= queries[i].length <= 3
  • 1 <= queries[i][0] <= 2
  • 1 <= x, sz <= min(5 * 104, 3 * queries.length)
  • 输入保证操作 1 中,x 处不会有障碍物。
  • 输入保证至少有一个操作类型 2 。

解法

方法一:树状数组 + 有序集合

思考

障碍只增不减,询问某前缀内能否放下长 \(sz\) 的块。在线对每个询问扫空隙是 \(O(q^2)\)

倒序处理可把「插入障碍」变成「删除障碍」,删除只会使空隙变大。树状数组维护「以某坐标为右端的空隙」的前缀最大值。

先把全部障碍(加哨兵)放进有序集合并写入空隙,再倒序:询问时看 \(x\) 左侧最大空隙与尾巴 \(x-pre\);删除时把后继空隙更新为 \(nxt-pre\)。答案再反转回原序。

障碍物只会增加,因此可以离线倒序处理询问,将「加入障碍物」转化为「删除障碍物」。删除后相邻空隙只会变大,而树状数组可以高效维护前缀最大值。

我们先将所有障碍物放入有序集合,并在两端加入哨兵 \(0\)\(m+1\)\(m\) 为出现过的最大坐标)。对每一对相邻障碍物 \(x_1, x_2\),在树状数组的 \(x_2\) 位置更新空隙长度 \(x_2 - x_1\)

然后倒序遍历询问:

  • 类型 \(2\):找到不超过 \(x\) 的最后一个障碍物 \(pre\)。若 \([0, pre]\) 内的最大空隙或尾部落在 \((pre, x]\) 的空隙不小于 \(sz\),则可以放置物块。
  • 类型 \(1\):删除障碍物 \(x\),并将后继 \(nxt\) 处的空隙更新为 \(nxt - pre\)

时间复杂度 \(O(q \times \log m)\),空间复杂度 \(O(m)\)。其中 \(q\) 是询问数,\(m\) 是坐标的最大值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class BinaryIndexedTree:
    def __init__(self, n: int):
        self.n = n
        self.c = [0] * (n + 1)

    def update(self, x: int, v: int):
        while x <= self.n:
            self.c[x] = max(self.c[x], v)
            x += x & -x

    def query(self, x: int) -> int:
        mx = 0
        while x:
            mx = max(mx, self.c[x])
            x -= x & -x
        return mx


class Solution:
    def getResults(self, queries: List[List[int]]) -> List[bool]:
        m = max(q[1] for q in queries)
        sl = SortedList([0, m + 1])
        for q in queries:
            if q[0] == 1:
                sl.add(q[1])
        tree = BinaryIndexedTree(m + 1)
        for x1, x2 in pairwise(sl):
            tree.update(x2, x2 - x1)
        ans = []
        for q in reversed(queries):
            x = q[1]
            if q[0] == 1:
                i = sl.index(x)
                tree.update(sl[i + 1], sl[i + 1] - sl[i - 1])
                sl.remove(x)
            else:
                i = sl.bisect_right(x)
                pre = sl[i - 1]
                ans.append(tree.query(pre) >= q[2] or x - pre >= q[2])
        return ans[::-1]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class BinaryIndexedTree {
    private int n;
    private int[] c;

    public BinaryIndexedTree(int n) {
        this.n = n;
        c = new int[n + 1];
    }

    public void update(int x, int v) {
        while (x <= n) {
            c[x] = Math.max(c[x], v);
            x += x & -x;
        }
    }

    public int query(int x) {
        int mx = 0;
        while (x > 0) {
            mx = Math.max(mx, c[x]);
            x -= x & -x;
        }
        return mx;
    }
}

class Solution {
    public List<Boolean> getResults(int[][] queries) {
        int m = 0;
        for (int[] q : queries) {
            m = Math.max(m, q[1]);
        }
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(0);
        ts.add(m + 1);
        for (int[] q : queries) {
            if (q[0] == 1) {
                ts.add(q[1]);
            }
        }
        BinaryIndexedTree tree = new BinaryIndexedTree(m + 1);
        int pre = 0;
        for (int x : ts) {
            if (x > 0) {
                tree.update(x, x - pre);
            }
            pre = x;
        }
        List<Boolean> ans = new ArrayList<>();
        for (int i = queries.length - 1; i >= 0; --i) {
            int[] q = queries[i];
            int x = q[1];
            if (q[0] == 1) {
                int nxt = ts.higher(x);
                tree.update(nxt, nxt - ts.lower(x));
                ts.remove(x);
            } else {
                int p = ts.floor(x);
                ans.add(tree.query(p) >= q[2] || x - p >= q[2]);
            }
        }
        Collections.reverse(ans);
        return ans;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class BinaryIndexedTree {
private:
    int n;
    vector<int> c;

public:
    BinaryIndexedTree(int n) {
        this->n = n;
        c.resize(n + 1);
    }

    void update(int x, int v) {
        while (x <= n) {
            c[x] = max(c[x], v);
            x += x & -x;
        }
    }

    int query(int x) {
        int mx = 0;
        while (x > 0) {
            mx = max(mx, c[x]);
            x -= x & -x;
        }
        return mx;
    }
};

class Solution {
public:
    vector<bool> getResults(vector<vector<int>>& queries) {
        int m = 0;
        for (auto& q : queries) {
            m = max(m, q[1]);
        }
        set<int> ts{0, m + 1};
        for (auto& q : queries) {
            if (q[0] == 1) {
                ts.insert(q[1]);
            }
        }
        BinaryIndexedTree tree(m + 1);
        int pre = 0;
        for (int x : ts) {
            if (x) {
                tree.update(x, x - pre);
            }
            pre = x;
        }
        vector<bool> ans;
        for (int i = queries.size() - 1; i >= 0; --i) {
            int x = queries[i][1];
            if (queries[i][0] == 1) {
                auto it = ts.find(x);
                tree.update(*next(it), *next(it) - *prev(it));
                ts.erase(it);
            } else {
                auto it = prev(ts.upper_bound(x));
                ans.push_back(tree.query(*it) >= queries[i][2] || x - *it >= queries[i][2]);
            }
        }
        ranges::reverse(ans);
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
func getResults(queries [][]int) []bool {
    m := 0
    for _, q := range queries {
        m = max(m, q[1])
    }
    st := redblacktree.New[int, struct{}]()
    st.Put(0, struct{}{})
    st.Put(m+1, struct{}{})
    for _, q := range queries {
        if q[0] == 1 {
            st.Put(q[1], struct{}{})
        }
    }
    tree := newBinaryIndexedTree(m + 1)
    it := st.Iterator()
    it.Next()
    pre := it.Key()
    for it.Next() {
        x := it.Key()
        tree.update(x, x-pre)
        pre = x
    }
    ans := []bool{}
    for i := len(queries) - 1; i >= 0; i-- {
        q := queries[i]
        x := q[1]
        if q[0] == 1 {
            nxt, _ := st.Ceiling(x + 1)
            p, _ := st.Floor(x - 1)
            st.Remove(x)
            tree.update(nxt.Key, nxt.Key-p.Key)
        } else {
            node, _ := st.Floor(x)
            p := node.Key
            ans = append(ans, tree.query(p) >= q[2] || x-p >= q[2])
        }
    }
    slices.Reverse(ans)
    return ans
}

type binaryIndexedTree struct {
    n int
    c []int
}

func newBinaryIndexedTree(n int) *binaryIndexedTree {
    return &binaryIndexedTree{n: n, c: make([]int, n+1)}
}

func (t *binaryIndexedTree) update(x, v int) {
    for x <= t.n {
        t.c[x] = max(t.c[x], v)
        x += x & -x
    }
}

func (t *binaryIndexedTree) query(x int) int {
    mx := 0
    for x > 0 {
        mx = max(mx, t.c[x])
        x -= x & -x
    }
    return mx
}

评论