題目連結
線段樹解法
#include <stdio.h>#include <algorithm>using namespace std;const int maxn = 100010;struct node{ int l, r, mid, minn;}tree[maxn<<2];int a[maxn];void build(int l, int r, int o){ tree[o].l = l; tree[o].r = r; int m = (l+r) >> 1; tree[o].mid = m; if (l == r) { tree[o].minn = a[l]; return ; } build(l, m, o<<1); build(m+1, r, (o<<1)+1); tree[o].minn = min(tree[o<<1].minn, tree[(o<<1)+1].minn);}int query(int l, int r, int o){ if (tree[o].l == l && tree[o].r == r) return tree[o].minn; if (r <= tree[o].mid) return query(l, r, o<<1); else if (l > tree[o].mid) return query(l, r, (o<<1)+1); else return min(query(l, tree[o].mid, o<<1), query(tree[o].mid+1, r, (o<<1)+1));}int main(){ int t, n, m, l, r; scanf("%d",&t); for (int k = 1; k <= t; k++) { scanf("%d%d",&n, &m); for (int i = 1; i <= n; i++) scanf("%d",&a[i]); build(1, n, 1); printf("Case %d:\n",k); while (m--) { scanf("%d %d",&l, &r); printf("%d\n",query(l, r, 1)); } } return 0;}
Sparse-Table 演算法 劉汝佳 訓練指南 p197
ST演算法:先是預先處理部分(構造RMQ數組),DP處理。假設b是所求區間最值的數列,dp[i][j] 表示從i到i+2^j -1中最值(從i開始持續2^j個數)。即dp[i][j]=min{dp[i][j-1],dp[i+2^(j-1)][j-1]},或者dp[i][j]=max{dp[i][j-1],dp[i+2^(j-1)][j-1]},這個過程的複雜度為:O(n(longn))
接著就是查詢最值了,可以通過在O(1)完成查詢。就是將查詢區間[s,v],分成兩個2^k的區間。
這裡只要知道這種演算法即可,因為資料量過大,都編譯不通過,不過思想演算法沒有任何問題。
解題代碼
#include <stdio.h>#include <algorithm>using namespace std;const int maxn = 100010;int a[maxn];int d[maxn][maxn];void rmqinit(int n){ for (int i = 1; i <= n; i++) d[i][0] = a[i]; for (int j = 1; (1<<j) <= n; j++) { for (int i = 1; i+j-1 <= n; i++) d[i][j] = min(d[i][j-1], d[i+(1<<(j-1))][j-1]); }}int rmq(int l, int r){ int k = 0; while ((1<<(k+1)) <= r-l+1) k++; return min(d[l][k], d[r-(1<<k)+1][k]);}int main(){ int t, n, m, l, r; scanf("%d",&t); for (int k = 1; k <= t; k++) { scanf("%d%d",&n, &m); for (int i = 1; i <= n; i++) scanf("%d",&a[i]); rmqinit(n); printf("Case %d:\n",k); while (m--) { scanf("%d %d",&l, &r); printf("%d\n",rmq(l, r)); } } return 0;}