牛客多校2021(五)K.King of Range(ST表、雙指標)
阿新 • • 發佈:2021-08-13
-
題意:給出一個序列,問其存在多少個子序列(子序列肯定連續~)滿足子序列中最大值與最小值的差大於k。
-
思路:ST表預處理 + 雙指標維護區間。
-
解析:以下用\(maxv, minv\)表示該區間最大值和最小值,假設當遍歷到區間[l, r - 1]時,\(maxv - minv \leq k\)說明該區間內不可能存在子區間滿足條件,但當遍歷到[l, r]區間時, \(maxv - minv > k\) 說明[l, r]區間滿足條件, 並且[l, r + 1],[l, r + 2]...[l, n]一定都滿足條件。
那麼我們可以首先用ST表預處理出所有區間的\(maxv, minv\)
細節:此題在雙指標維護區間過程中需要多次查詢區間最大值和最小值,若log2()函式是直接用函式庫的則會T掉,log2()函式時間複雜度為log(n),所以需要預處理\(log_2(1)\)~\(log_2(n)\)
-
程式碼:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; const int N = 1e5 + 5; const int M = 20; int a[N], f[N][M], g[N][M]; //f(i, j)表示[i, i + 2^j - 1]最小值, g(i, j)表示最大值 int lg[N]; //log2(x) int n, m, k; void fun() //預處理log2()函式 { lg[0] = -1; for(int i = 1; i <= n; i++) lg[i] = lg[i >> 1] + 1; } void st_init() { for(int i = 1; i <= n; i++) { f[i][0] = a[i]; g[i][0] = a[i]; } for(int j = 1; j <= log2(n); j++) { for(int i = 1; i <= n + 1 - (1 << j); i++) { f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]); } } } int query_min(int l, int r) { int len = r - l + 1; int k = lg[len]; return min(f[l][k], f[r - (1 << k) + 1][k]); } int query_max(int l, int r) { int len = r - l + 1; int k = lg[len]; return max(g[l][k], g[r - (1 << k) + 1][k]); } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); st_init(); fun(); while(m --) { scanf("%d", &k); ll num = 0; for(int i = 1, j = 1; i <= n; i++) { while( (i >= j || query_max(i, j) - query_min(i, j) <= k) && (j < n) ) j ++; if(query_max(i, j) - query_min(i, j) > k) //題目條件 num += ll(n - j + 1); } printf("%lld\n", num); } return 0; }