D Difference (二分 + 單調佇列)
阿新 • • 發佈:2022-05-29
D Difference (二分 + 單調佇列)
https://ac.nowcoder.com/acm/contest/34866/D
題意
給你長度為n的序列 和一個k
一個區段值滿足: f(l, r) = (max - min) * (r - l + 1); 其中max min是區間最大最小值
要求輸出第k大的區間值
思路
因為資料比較大 容易超時 可以考慮二分答案
然後去判斷對於一個答案有幾個大於等於它的 找到存在k個大於等於它的答案
(二分答案的複雜度是OlogV)
尋找一個大於等於ans的區間值的個數 可以考慮用單調佇列
對於一個區間如果r固定[l, r]的區間值肯定比[l-1, r]大 因為[l, r]覆蓋[l-1, r]前一個區間的區間長度比後一個大 而max-min肯定大於等於後一個區間
而要求某個區間的最大最小值 可以用單調佇列分別維護
固定有邊界 然後每次找到左邊界最右的且滿足區間值等於當前二分的答案 的l 對於這個右邊界 貢獻就是l
遍歷右邊界1-n 將貢獻累加起來 與k做比較即可
#include<bits/stdc++.h> #include<unordered_map> #include<algorithm> #include<map> #define ll long long #define ull unsigned long long #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; const ll inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-4; const ll N = 5e5 + 5; const int M = 1e6 + 5; const ll mod = 1e9 + 7; ll n, a[N], k, que[N]; ll lmi, lmx, rmi, rmx; ll quemi[N], quemx[N];//儲存佇列中第i個元素在a陣列中的下標 //判斷函式 bool check(ll x) { //單調佇列 ll l = 1, ans = 0; lmi = 1, lmx = 1;//一開始左邊初始化為1 右邊初始化為0 便於第一次操作 rmi = 0, rmx = 0; for (int i = 1; i <= n; i++) { //區間最大 最小值 佇列頭是最大或最小 //每次判斷佇列尾是否比要放進來的值小或大 根據情況彈出佇列 保證佇列隊頭是最值後面是單調的 while (lmi <= rmi && a[quemi[rmi]] >= a[i]) rmi--; //自己進佇列 quemi[++rmi] = i; while (lmx <= rmx && a[quemx[rmx]] <= a[i]) rmx--; quemx[++rmx] = i; while (l <= i) { //判斷當前區間是否是要找的那個區間 //如果區間值大於等於x 說明區間還可以再縮短 if ((a[quemx[lmx]] - a[quemi[lmi]]) * (i - l + 1) >= x) { //判斷隊頭是否是l邊界 如果是向右縮短的同時 佇列隊頭彈出一個元素 if (quemx[lmx] == l) lmx++; if (quemi[lmi] == l) lmi++; l++; } else break; } ans += l - 1; } if (ans >= k) return true; return false; } void solve() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> a[i]; } //二分答案 ll l = 0, r = 1e15; ll ans = 0; while (l <= r) { ll mid = (l + r) / 2; if (check(mid)) { ans = mid; l = mid + 1; } else r = mid - 1; } cout << ans << "\n"; } signed main() { IOS; int t = 1; //cin >> t; while (t--) { solve(); } }