Atcoder Beginner Contest 107 D Median of Medians(二分答案)(中位數)
阿新 • • 發佈:2018-12-13
題意
給出一個序列,求這個序列所有子序列中位數的中位數
思路
直接考慮非常fake
換個角度,某個數要成為中位數的中位數,那麼中位數大於等於他的序列必須佔總序列數的至少1/2,而最終答案是符合這個條件的最大的數。於是二分答案。
如何寫check函式呢?因為選出mid之後,序列中所有數的具體數值已經不重要了,所以可以把所有大於等於mid的數看作1,小於的看作-1,則某個序列的中位數如果大於等於mid,那這個區間裡1和-1的和大於等於0。這裡如果對1和-1的序列求字首和,那麼問題轉化成求d[i]<=d[j],且i < j的對數,可以用歸併或者樹狀陣列求逆序對的方法做。
要注意的是各種等於能不能取到,各種向上取整向下取整。
程式碼
//歸併寫法
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const ll INF = 1e18+10;
const ll N = 100010;
ll n, a[N], lim, d[N], c[N], res;
void chkMax(ll &x, ll y){if (x < y) x = y;}
void chkMin(ll &x, ll y){if (x > y) x = y; }
void Msort(ll l, ll r)
{
if (l == r) return;
ll mid = (l+r)>>1;
Msort(l, mid);
Msort(mid+1, r);
ll i = l, j = mid+1, k = l;
while (i <= mid && j <= r){
if (d[i] <= d[j]){
c[k++] = d[i++];
res += r-j+1;
}
else
c[k++] = d[j++];
}
while (i <= mid) c[k++] = d[i++];
while (j <= r) c[k++] = d[j++];
for (ll o = l; o <= r; o++)
d[o] = c[o];
}
bool Check(ll mid)
{
d[0] = 0;
for (ll i = 1; i <= n; i++){
d[i] = d[i-1];
if (a[i] >= mid)
d[i]++;
else
d[i]--;
}
res = 0;
Msort(0, n);
if (res >= lim) return true;
return false;
}
int main()
{
cin >> n;
lim = (n*(n+1)/2+1)/2;
ll l = INF, r = -INF, mid, ans;
for (ll i = 1; i <= n; i++){
cin >> a[i];
chkMax(r, a[i]);
chkMin(l, a[i]);
}
while (l <= r){
mid = (l+r)>>1;
if (Check(mid))
ans = mid, l = mid+1;
else
r = mid-1;
}
cout << ans;
return 0;
}