【題解】Zerojudge d539 - 區間 MAX

題目大意

給定一個長度為 $n$ 的數列 $a_1, \dots, a_n$ 和 $q$ 筆查詢。每次查詢 $a_l, \dots, a_r$ 中的最大值。
  • $1 \leq q \leq n \leq 5 \cdot 10^5$
  • $1 \leq a_i < 2^{31}$

題解

本題是經典的 RMQ 問題。RMQ 有許多做法,使用資料結構的話可以使用稀疏表 (sparse table) 或是線段樹去實作,甚至也能夠使用分塊。以下提供這三種資料結構的 code 和解說。

1. 稀疏表 (sparse table)

只能處理靜態 (不帶更新) 的操作。
#include <bits/stdc++.h>

template<class S, S (*op)(S, S)>
struct sparse_table {
public:
	sparse_table() {}
	explicit sparse_table(const std::vector<S>& a) {
		n = (int) a.size();
		int max_log = std::__lg(n) + 1;
		mat.resize(max_log);
		mat[0] = a;
		for(int j = 1; j < max_log; ++j) {
			mat[j].resize(n - (1 << j) + 1);
			for(int i = 0; i <= n - (1 << j); ++i) {
				mat[j][i] = op(mat[j - 1][i], mat[j - 1][i + (1 << (j - 1))]);
			}
		}
	}

	S prod(int from, int to) const {
		assert(0 <= from && from <= to && to <= n - 1);
		int lg = std::__lg(to - from + 1);
		return op(mat[lg][from], mat[lg][to - (1 << lg) + 1]);
	}

private:
	int n;
	std::vector<std::vector<S>> mat;
};

using namespace std;

int op_max(int a, int b) { return max(a, b); }

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin >> n;
	vector<int> a(n);
	for(int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sparse_table<int, op_max> st(a);
	int q;
	cin >> q;
	while(q--) {
		int l, r;
		cin >> l >> r;
		l--, r--;
		if(l > r) {
			swap(l, r);
		}
		cout << st.prod(l, r) << "\n";
	}
	return 0;
}

2. 線段樹

以下的程式碼為 zkw 線段樹,是一種使用迴圈實作的版本。因為沒有使用到遞迴,因此常數很小。
#include <bits/stdc++.h>

template<class S, S (*e)(), S (*op)(S, S)>
struct segtree {
public:
    segtree() {}
    explicit segtree(int _n) : segtree(std::vector<S>(_n, e())) {}
    explicit segtree(const std::vector<S>& a): n(a.size()) {
        log = std::__lg(2 * n - 1);
        size = 1 << log;
        d.resize(size * 2, e());
        for(int i = 0; i < n; ++i) {
            d[size + i] = a[i];
        }
        for(int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }
    
    void set(int p, S val) {
        assert(0 <= p && p < n);
        p += size;
        d[p] = val;
        for(int i = 1; i <= log; ++i) {
            update(p >> i);
        }
    }

    S get(int p) const {
        assert(0 <= p && p < n);
        return d[p + size];
    }

    S operator[](int p) const { return get(p); }
    
    S prod(int l, int r) const {
        assert(0 <= l && l <= r && r <= n);
        S sml = e(), smr = e();
        for(l += size, r += size; l < r; l >>= 1, r >>= 1) {
            if(l & 1) {
                sml = op(sml, d[l++]);
            }
            if(r & 1) {
                smr = op(d[--r], smr);
            }
        }
        return op(sml, smr);
    }

    S all_prod() const { return d[1]; }

    template<bool (*f)(S)> int max_right(int l) {
        return max_right(l, [](S x) { return f(x); });
    }

    template<class F> int max_right(int l, F f) {
        assert(0 <= l && l <= n);
        assert(f(e()));
        if(l == n) {
            return n;
        }
        l += size;
        S sm = e();
        do {
            while(~l & 1) {
                l >>= 1;
            }
            if(!f(op(sm, d[l]))) {
                while(l < size) {
                    push(l);
                    l <<= 1;
                    if(f(op(sm, d[l]))) {
                        sm = op(sm, d[l++]);
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l++]);
        } while((l & -l) != l);
        return n;
    }

    template<bool (*f)(S)> int min_left(int r) {
        return min_left(r, [](S x) { return f(x); });
    }

    template<class F> int min_left(int r, F f) {
        assert(0 <= r && r <= n);
        assert(f(e()));
        if(r == 0) {
            return 0;
        }
        r += size;
        S sm = e();
        do {
            r--;
            while(r > 1 && (r & 1)) {
                r >>= 1;
            }
            if(!f(op(d[r], sm))) {
                while(r < size) {
                    push(r);
                    r = 2 * r + 1;
                    if(f(op(d[r], sm))) {
                        sm = op(d[r--], sm);
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while((r & -r) != r);
        return 0;
    }
    
protected:
    int n, size, log;
    std::vector<S> d;

    void update(int v) {
        d[v] = op(d[2 * v], d[2 * v + 1]);
    }

    virtual void push(int p) {}
};

using namespace std;

int e() { return 0; }
int op_max(int a, int b) { return max(a, b); }

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin >> n;
	vector<int> a(n);
	for(int i = 0; i < n; i++) {
		cin >> a[i];
	}
	segtree<int, e, op_max> seg(a);
	int q;
	cin >> q;
	while(q--) {
		int l, r;
		cin >> l >> r;
		l--, r--;
		if(l > r) {
			swap(l, r);
		}
		cout << seg.prod(l, r + 1) << "\n";
	}
	return 0;
}

3. 分塊

使用分塊的時候塊的大小可以視情況調整。以下的 code 塊的大小取 $650$ 時跑 $0.9$s,取 $700$ 和 $800$ 跑 $0.4$s。
#include <bits/stdc++.h>
using namespace std;

const int BLOCK_SIZE = 700;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin >> n;
	vector<int> a(n);
	for(int i = 0; i < n; i++) {
		cin >> a[i];
	}
	vector<int> block((n + BLOCK_SIZE - 1) / BLOCK_SIZE);
	auto get_block_id = [](int i) {
		return i / BLOCK_SIZE;
	};
	for(int i = 0; i < n; i++) {
		block[get_block_id(i)] = max(block[get_block_id(i)], a[i]);
	}
	int q;
	cin >> q;
	while(q--) {
		int l, r;
		cin >> l >> r;
		l--, r--;
		if(l > r) {
			swap(l, r);
		}
		int lid = get_block_id(l), rid = get_block_id(r);
		int ans = 0;
		if(lid == rid) {
			for(int i = l; i <= r; i++) {
				ans = max(ans, a[i]);
			}
		} else {
			for(int i = l; i < n && lid == get_block_id(i); i++) {
				ans = max(ans, a[i]);
			}
			for(int i = r; i >= 0 && rid == get_block_id(i); i--) {
				ans = max(ans, a[i]);
			}
			for(int i = lid + 1; i < rid; i++) {
				ans = max(ans, block[i]);
			}
		}
		cout << ans << "\n";
	}
	return 0;
}

如果本文對您有幫助的話幫忙點擊廣告和分享吧!

© 若無特別註明,本站文章皆由 WeakMouse's Coding Blog 原創 ,轉載引用本文前請先留言告知。本文轉載請註明文章源自 WeakMouse's Coding Blog ,作者 ,並附上原文連結: 【題解】Zerojudge d539 - 區間 MAX

張貼留言

0 留言