/**
(a)
|SORT(A,p,r)
| IF(p >= r) RETURN;
| m <- (p + r) / 2
| x <- MEDIAN(A,p,r)
| PARTITION(A,p,r,x)
| SORT(A,p,m-1)
| SORT(A,m+1,r)
|[]
Justification:
The algorithm works as following: I put the median on the correct position and then, i do the same to the two halves.
Thus, in the end I will get all possible medians in their correct position, and this means the array is sorted.
Time complexity: T(n) = 2*T(n/2) + O(N) => O(NlogN)
(b)
|SELECT(A,p,r,nth) /// returns the elemen which is on the n'th position in the array
| IF( p >= r ) RETURN A[p + nth]
| m <- (p + r) / 2
| x <- MEDIAN(A,p,r)
| IF m = p + nth THEN RETURN x
| IF m > p nth THEN RETURN SELECT(A,p,m - 1,nth)
| RETURN SELECT(A,m + 1,r,nth - (m - p) - 1)
|[]
Justification:
The algorithm works as following: I search the interval [p,r] such that n'th element is it's middle. Thus, if n'th is current middle, i return it,
otherwise, if nth is bigger than my middle, i search the middle in the right half, otherwise in the left half.
If I move in the left half, the nth element stays the same.
If I move in the right half, I won't need nth element, but nth - (m - p) - 1 element, since i removed (m + 1 - p) elements in the range [p..r]
Time complexity: T(n) = T(n/2) + O(N) => O(N)
(c)
To use a single MEDIAN call, we hope that it is the middle of the [p,q] interval.
If it is not, then we make it be the middle of the interval, by adding -INF at the beginning, or +INF at the ending of A.
This is also linear time complexity and works as following:
While nth > m, I add ininity at the end and update the new middle and the new q (length). This will shift the middle to the right until I reach the correct position
While nth < m, I add infinity at the beginning and update the new ending, the new middle and the new nth position. As nth position grows by 1 each time I add -INF and the middle grows by 1 only after two steps, nth will reach the middle.
Then, we just apply MEDIAN.
Time complexity: the complexity of median. However, this has to be implemented using the partitioning and either randomized pivot, either buckets of 5, the complexity is O(N).
For further details, check my C++ implementations.
*/
#include <bits/stdc++.h>
#define Nmax 3666013
#define DIM 666013
using namespace std;
char buffer[DIM];
int PZ = DIM - 1;
int N,K;
vector<int> A;
/// This API will partition vector V according to the boundaries [li,lf] and the value X
void Parser(int &A)
{
A = 0;
while('0' > buffer[PZ] || buffer[PZ] > '9')
if(++PZ == DIM)
fread(buffer,1,DIM,stdin),PZ = 0;
while('0' <= buffer[PZ] && buffer[PZ] <= '9')
{
A = A * 10 + buffer[PZ] - 48;
if(++PZ == DIM)
fread(buffer,1,DIM,stdin),PZ = 0;
}
}
int Partition(vector<int> &V,int li,int lf,int X){
int swapd = 0, p = 0;
for(int i = li ; i <= lf; ++i){
if(V[i] == X && ! swapd)
swap(V[i],V[lf]),swapd = 1;
if(V[i] < X)
swap(V[li++],V[i]);
}
swap(V[lf],V[li]);
return li;
}
/// This API will sort the elements using an O(N^2) sorting method
void Introsort(vector<int> &a,int li,int lf)
{
for(int i = li ; i <= lf; ++i)
for(int j = i + 1; j <= lf; ++j)
if(a[i] > a[j])
swap(a[i],a[j]);
}
/// This API will return the element at pz'th position in a, in O(N) time.
int Med(vector<int> &a,int li,int lf,int pz)
{ /// O(N) time complexity, proved by T(N) = T(N/5) + T(7N/10) + bn
if(lf - li + 1 <= 5){
Introsort(a,li,lf);
return a[li + pz];
}
vector<int> aux1,aux2;
for(int i = li; i <= lf; ++i){
aux1.push_back(a[i]);
if((i-li) % 5 == 4){
Introsort(aux1,0,4);
aux2.push_back(aux1[2]);
aux1.clear();
}
}
if(aux1.size()){
Introsort(aux1,0,aux1.size()-1);
aux2.push_back( aux1[(aux1.size() - 1) / 2 ]);
}
int val = Med(aux2,0,aux2.size() - 1, (aux2.size() - 1)/2);
int m = Partition(a,li,lf,val);
for(int i = li; i <= m; ++i)
if(a[i] > a[m])
printf("gresita partitionarea");
for(int i = m + 1; i <= lf; ++i)
if(a[m] > a[i])
printf("gresita partitionarea");
if(m == li + pz)
return a[m];
if(m > li + pz)
return Med(a,li,m - 1,pz);
return Med(a,m + 1,lf,pz - (m - li) - 1);
}
///This API implements MEDIAN'
int Median(vector<int> &a,int li, int lf)
{
int m = li + ((lf - li) >> 1);
int k = Med(a,li,lf, m - li);
return a[m];
}
///This API sorts the elements in O(NlogN) time
void NiceSort(vector<int> &a, int li,int lf)
{ /// T(n) = 2*T(n/2) + O(N) => O(NlogN)
if(li >= lf)
return;
int m = li + ((lf - li) >> 1);
int k = Med(a,li,lf, m - li);
NiceSort(a,li, m - 1);
NiceSort(a,m + 1, lf);
}
///This API selects the pz'th element in O(N) time
int Select(vector<int> &a, int li, int lf, int pz)
{ /// T(n) = T(n/2) + O(N) => O(N)
if(li >= lf)
return a[li + pz];
int m = li + ((lf - li) >> 1);
int k = Median(a,li,lf);
if(m == li + pz)
return a[m];
if(m > li + pz)
return Select(a,li,m - 1,pz);
return Select(a,m+1,lf,pz - (m - li) - 1);
}
/// This API will select the pz'th element in O(N) time, with just a single call of median
vector<int> New;
int PaddedSelect(vector<int> &a, int li, int lf,int pz)
{
int m = li + ((lf - li) >> 1);
if(pz == m)
New = a;
else
if(pz > m){
New = a;
while(pz > m)
{
New.push_back(2147483647);
++lf;
m = li + ((lf - li) >> 1);
}
}
else
{
while(pz < m)
{
New.push_back(-2147483648);
++pz;
++lf;
m = li + ((lf - li) >> 1);
}
for(auto it : a)
New.push_back(it);
}
/// Done the paddings
printf("%d\n",New[lf/2]);
Median(New,li,lf);
return New[lf/2];
}
void Read()
{
Parser(N),Parser(K);
A.resize(N);
for(int i = 0; i < N; ++i)
Parser(A[i]);
}
void Print()
{
///NiceSort(A,0,N-1);
///printf("%d\n",Median(A,0,N-1));
printf("%d\n",Select(A,0,N-1,K-1));
///printf("%d\n",PaddedSelect(A,0,N-1,K-1));
///for(auto it : A)
///printf("%d ",it);
///Partition(A,0,N-1,5);
}
int main()
{
freopen("sdo.in","r",stdin);
freopen("sdo.out","w",stdout);
Read();
Print();
return 0;
}