题解:中位数

洛谷P1168

Posted by TH911 on December 14, 2024

题目传送门

权值树状数组

很好理解,$i$ 枚举 $1\sim n$,每次往权值状数组中加入 $a_i$,这样我们就能知道对于一个数 $x$,$a[1…i]$ 中小于等于 $x$ 的数的个数。

  • 二分答案,在值域上二分第 $k$ 大的数,时间复杂度,$\mathcal O(n\log^2 n)$。

  • 倍增查询最大的 $pl$ 使得 $a[1…i]$ 中有 $k-1$ 个数小于等于 $pl$,$pl+1$ 即第 $k$ 大的数。

    时间复杂度:$\mathcal O(n\log n)$。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
const int N=100000;
int n,m,a[N+1],b[N+1],c[N+1];
int lowbit(int x){
	return x&-x;
}
void add(int x,int k){
	while(x<=m){
		c[x]+=k;
		x+=lowbit(x);
	}
}
//倍增查询
int query(int k){
	int cnt=0,pl=0;
	for(int i=1<<20;i>0;i/=2){
		pl+=i;
		if(pl>m||cnt+c[pl]>=k)pl-=i;
		else cnt+=c[pl];
	}return pl+1;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",a+i);
		b[i]=a[i];
	}sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
	for(int i=1;i<=n;i++){
		add(a[i],1);
		if(i%2)printf("%d\n",b[query((i+1)/2)]);
	}
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

权值线段树

这也是此处的方法,考虑到此方法复杂、代码冗长、常数较大,此处不予代码。

事实上,与上一种方法几乎一模一样,区别仅仅在于将树状数组改为线段树。

对顶堆

我们维护一个大根堆 $x$ 和一个小根堆 $y$,让 $x$ 中的元素小于 $y$ 中的元素,即 $x.top()<y.top()$。

每次插入元素 $a_i$ 时,若 $a_i \leq x.top()$,则将 $a_i$ 加入 $x$,否则将 $a_i$ 加入 $y$。

而答案即调整 $x,y$ 的元素数量,使得 $x.size()=y.size()+1$,此时 $x.top()$ 即答案。


其实堆可以说是一个弱化版的排序,而数据结构的本质就是不去处理不需要的信息(比如懒标记、堆),因此复杂度得以提升。

时间复杂度:$\mathcal O(n\log n)$。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
int n,pl;
priority_queue<int>x;
priority_queue<int,vector<int>,greater<int> >y;
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	scanf("%d",&pl);
	x.push(pl);
	printf("%d\n",pl); 
	for(int i=2;i<=n;i++){
		scanf("%d",&pl);
		if(pl>x.top())y.push(pl);
		else x.push(pl);
		while(y.size()>x.size()){
			x.push(y.top());
			y.pop();
		}
		while(x.size()>y.size()+1){
			y.push(x.top());
			x.pop();
		}
		if(i%2)printf("%d\n",x.top());
	}
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

插入排序

这是个什么鬼方法……数据过水(

声明:本方法时间复杂度为 $\mathcal O\left(n^2\right)$,但因为常数较小且数据过水,可以通过此题

依题意模拟进行插入排序即可。

插入时间复杂度:$\mathcal O(n)$。

以下代码使用二分求出了应该插入的位置,然而还是 $\mathcal O(n)$。

参考代码(数组)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
int n,pl;
int a[100001];
void insert(int x,int p,int size){
	for(int i=size+1;i>=p;i--)a[i+1]=a[i];
	a[p]=x;
} 
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&pl);
		insert(pl,upper_bound(a,a+i,pl)-a,i-1);
		if(i%2){
			printf("%d\n",a[i/2+1]);
		}
	}
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

参考代码(vector)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
int n,pl;
vector<int>a;
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&pl);
		a.insert(upper_bound(a.begin(),a.end(),pl),pl);
		if(i%2){
			printf("%d\n",a[a.size()/2]);
		}
	}
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}