1. 程式人生 > 其它 >D Difference (二分 + 單調佇列)

D Difference (二分 + 單調佇列)

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();
	}
}