题解:AT_arc101_b [ABC107D] Median of Medians
我敢打赌,这肯定是洛谷这道题里最详细的题解了。
思路:
写这道题的时候可以先去 P3031 看一下,这篇题解就以此为突破口。
对于 P3031 这道题,我们可以先打暴力。
P3031 の 40 分解法:
我们学过,如果想要找子串,我们可以枚举左端点和右端点,如下:
for(int l=1;l<=n;l++){
for(int r=l;r<=n;r++){
/*这里写中位数判断*/
}
}
于是我们这个暴力只需要注意判断子串中位数是否大于等于
我们将中位数判断部分记为函数 check(l,r)。那么我们改怎么写 check(l,r) 呢?
很明显,作为暴力,我们可以再用一个数组来存储
将
b 按升序排序得到数列b' 。此时,b' 的第M\ /\ 2\ +\ 1 个元素的值即为b 的中位数。这里,/ 表示向下取整的除法。
定义来源于题目。
判断完
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,x;
int a[1000005];
int b[1000005];
int check(int l,int r){
int m=0;
for(int i=l;i<=r;i++){
b[++m]=a[i];
}
sort(b+1,b+m+1);
return (b[m/2+1]>=x?1:0);
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
cin>>n>>x;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int ans=0;
for(int l=1;l<=n;l++){
for(int r=l;r<=n;r++){
ans+=check(l,r);
}
}
cout<<ans;
return 0;
}
P3031 の 80 分解法:
通过暴力得到
我们可以发现,对于每一个字串,如果希望
- 设
x 为数组b 中大于等于x 的数字个数,y 为数组b 中小于x 的数字个数,那么x,y 一定满足x-y\ge0 。
这个性质是怎么来的?我们可以想,排完序后,如果
这里也就间接的证明了 m/2,编译器会自动向下取整。
最后便是如何计算
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[1000005],sum[1000005];
int n,m,k,x;
signed main(){
cin>>n>>x;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>=x){
sum[i]=1;
}
else{
sum[i]=-1;
}
sum[i]+=sum[i-1];
}
int ans=0;
for(int l=1;l<=n;l++){
for(int r=l;r<=n;r++){
if(sum[r]-sum[l-1]>=0){
ans++;
}
}
}
cout<<ans;
return 0;
}
P3031 の 100 分解法:
拿下
我们观察一下
我们将其与
似乎在哪里见过这个式子,我们将
这下完全想起来了,这不就是 P1908 逆序对的颠倒版吗!
我们知道逆序对是可以用树状数组来记录之前比
接下来就是处理正负数的问题。很明显,数字区间是
最后还要注意存入时,第一步时 long long。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int b=100001;
int a[1000005],sum[1000005];
int tr[1000005];
int n,m,k,x;
int ans[1000005];
int lowbit(int x){
return x&-x;
}
void modify(int x,int v){
while(x<=210001){
tr[x]+=v;
x+=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x){
ans+=tr[x];
x-=lowbit(x);
}
return ans;
}
signed main(){
cin>>n>>x;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>=x){
sum[i]=1;
}
else{
sum[i]=-1;
}
}
modify(0+b,1);
int ans=0;
for(int i=1;i<=n;i++){
sum[i]+=sum[i-1];
ans+=query(sum[i]+b);
modify(sum[i]+b,1);
}
cout<<ans;
return 0;
}
AT_arc101_b の 100 分解法:
讲了这么久的 P3031,是时候该讲 AT_arc101_b 了。
我们可以发现,其实我们可以对这道题经行二分答案。为什么?我们的目标是找到一个
- 对于不同的
x ,较大的x 满足的概率绝对比较小的x 的低。
于是我们可以以
最重要的还属 check 函数。check 函数我们的代码可以沿用 P3031 的
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int b=100001;
int a[1000005],c[1000005],sum[1000005];
int tr[1000005];
int n,m,k;
int ans[1000005];
int lowbit(int x){
return x&-x;
}
void modify(int x,int v){
while(x<=210001){
tr[x]+=v;
x+=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x){
ans+=tr[x];
x-=lowbit(x);
}
return ans;
}
bool check(int x){
memset(tr,0,sizeof tr);
memset(sum,0,sizeof sum);
for(int i=1;i<=n;i++){
if(a[i]>=x){
sum[i]=1;
}
else{
sum[i]=-1;
}
}
modify(0+b,1);
int ans=0;
for(int i=1;i<=n;i++){
sum[i]+=sum[i-1];
ans+=query(sum[i]+b);
modify(sum[i]+b,1);
}
if(ans>=(n*(n+1)/2+1)/2) return 1;
return 0;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
c[i]=a[i];
}
sort(c+1,c+n+1);
int l=1,r=n;
while(l<r){
int mid=(l+r+1)/2;
if(check(c[mid])) l=mid;
else r=mid-1;
}
cout<<c[l];
return 0;
}
后话:
本篇题解编辑历时