B - K-th Number HDU - 6231[二分+尺取]
阿新 • • 發佈:2018-11-07
題意:給出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大的數字,二分思想可以降低超多複雜度。
/**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;
}