1. 程式人生 > >B - K-th Number HDU - 6231[二分+尺取]

B - K-th Number HDU - 6231[二分+尺取]

題意:給出n,m,k有n個數字,詢問這個陣列中所有區間第k大數字組成的陣列中第m大的數字。(好繞啊,希望能懂OAO)


題解:嘗試考慮一下我們如果已經知道一個數字之後,在陣列中大於有m以及多餘m個區間的第k大大於這個數字,那麼這個數字一定不可能是答案,根據這個我們可以確定了一個單調的性質,所以我們可以二分答案。如何去檢查有多少給個區間的第k大的數字大於當前數字的區間有多少個,這時候需要用一個尺取的方法來維護,我們在維護的過程中如果區間個數超過了m那麼當前的答案一定是偏小的,否則則是偏大的。
尺取如何工作:我們尺取區間的時候,如果某個區間的第k大大於當前的數字時候,我們之後加上後面的所有區間(有點含糊)的時候只會使得當前的第k大變為第(k+1)大或者(k+2)大等等。這樣就能o(n)的維護出有多少個區間的第k大大於當前數字,從而確定當前數字是否合法。


收穫:問題的模型有時候需要轉化一下,並不能生硬的去解題,如第m大可以轉化為有(m-1)個比m大的數字,二分思想可以降低超多複雜度。


a c   c o d e

ac\ code

/**hqx is the best**/
/*****陣列大小*****/
/**hqx is the best**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug cerr << "*" << endl;
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define
pre(i, a, b) for(int i = a; i >= b; i--)
#define met(a, b) memset(a, b, sizeof(a)) const int maxn = 1e6 + 10; const int inf = 0x3f3f3f3f; ll T, n, k, m, a[maxn], b[maxn]; bool check(ll mid) { ll l = 1, r = 0, num = 0; ll ans = 0; while(r <= n) { if(num < k) { if(a[r + 1] >= mid) num++; r++; } else { if(num == k) ans += (n - r + 1); if(a[l] >= mid) num--; l++; if(ans >= m) return true; } } return false; } int main() { scanf("%lld", &T); while(T--) { scanf("%lld%lld%lld", &n, &k, &m); rep(i, 1, n) { scanf("%lld", &a[i]); } memcpy(b, a, sizeof(a)); ll l = 1, r = n, ans; sort(b + 1, b + 1 + n); while(l <= r) { ll mid = (l + r) >> 1; if(check(b[mid])) { ans = b[mid]; l = mid + 1; } else { r = mid - 1; } } printf("%lld\n", ans); } return 0; }