题意
给出一个序列,求这个序列所有子序列中位数的中位数
思路
直接考虑非常fake
换个角度,某个数要成为中位数的中位数,那么中位数大于等于他的序列必须占总序列数的至少1/2,而最终答案是符合这个条件的最大的数。于是二分答案。
如何写check函数呢?因为选出mid之后,序列中所有数的具体数值已经不重要了,所以可以把所有大于等于mid的数看作1,小于的看作-1,则某个序列的中位数如果大于等于mid,那这个区间里1和-1的和大于等于0。这里如果对1和-1的序列求前缀和,那么问题转化成求d[i]<=d[j],且i < j的对数,可以用归并或者树状数组求逆序对的方法做。
要注意的是各种等于能不能取到,各种向上取整向下取整,还有区间的le, ri和二分的l, r。
另一个中位数题:nowcoder
代码
//归并写法
#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;
}