Задача из второго тура SNWS-10. Про медиану. Дано N (1<=N<=10^6) чисел. Числа добавляются в изначально пустой набор в заданном порядке. Требуется определять медиану после каждого добавления числа.
Я реализовал бинарное дерево поиска по Кормену. Однако получил TL на 11 тесте. Вот код:
struct binary_elem {
long key,size,left,right;
};
vector<binary_elem> BT;
binary_elem BT_init(long key) {
binary_elem res;
res.key=key;
res.size=1;
res.left=-1;
res.right=-1;
return res;
}
void BT_insert (binary_elem z) {
long y=-1,x=0;
BT.push_back(z);
long cur=BT.size()-1;
if (cur==0) return;
while (x!=-1) {
y=x;
BT[x].size++;
if (z.key<BT[x].key) x=BT[x].left; else x=BT[x].right;
}
if (y!=-1)
if (z.key<BT[y].key) BT[y].left=cur; else BT[y].right=cur;
}
long BT_select (long x, long i) {
long r;
while (true) {
if (BT[x].left==-1) r=1; else r=BT[BT[x].left].size+1;
if (i==r) return BT[x].key;
else if (i<r) { x=BT[x].left; } else {x=BT[x].right; i=i-r; }
}
}
int main(){
long n,num,i;
freopen("median.in","rt",stdin);
freopen("median.out","wt",stdout);
scanf("%d",&n);
for (i=0;i<n;i++) {
scanf("%d",&num);
BT_insert(BT_init(num));
long res = BT_select(0,(i+2)/2);
printf("%d ",res);
}
}
Подскажите плз, что не так. Или как ее можно решить по-другому. Я посмотрел, некоторые ее решили за 5-7 минут!
а я писал дерево отрезков, со сжатием координат и поиском k-той порядковой статистики за log(n). в первой попытке было TLE, потому что искал за log2 (n)
Все-таки придется эти загадочные деревья отрезков, Фенвика разобрать и понять. Хотя врубиться в эти читерские &, | никак не могу.
Про балансировку ничего конкретного в Кормене не сказано, а уж тем более если его надо балансировать на ходу.
Спс за комменты, буду пробовать дальше.
Идея такая - один сортит по возрастанию(пусть это будет s2), другой по убыванию(соответсвенно, s1). Медианный элемент будет в вершине s2. Нам только надо при добавлении нового элемента это поддерживать - т.е. смотреть, в какой сет добавить и, если надо, перекинуть с верхушки одного сета в другой.
Вроде вот это решение прошло:
freopen("median.in" ,"rt", stdin); freopen("median.out", "wt", stdout);
int i, n;
cin >> n;
multiset<int>::iterator p;
for (i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
a.insert(x);
if (i == 1) {
p = a.begin();
} else if (x < *p) {
if ((i-1)%2 == 1) --p;
} else {
if ((i-1)%2 == 0) ++p;
}
printf("%d ", *p);
}
fclose(stdin); fclose(stdout);
return 0;
Забавно, но эту задачу мы давали своим школьникам на полуфинале ВКОШП http://imcs.dvgu.ru/cats/main.pl?f=problem_text;cpid=710031;sid=;cid=706500
Ее вполне можно решить двумя кучами, если в языке слабая стандартная библиотека (впрочем, в современном мире это натяжка;) ).
Идея: будем решать эту задачу с конца. Даны 10^6 посортированных чисел, далее их удаляют в определённом порядке. После каждого удаления необходимо говорить медиану.
Ясно, что можно сделать двухсвязный список (который можно реализовать на массиве), чтобы хранить левое и правое живые числа. На каждом шаге бинпоиском (или за константу -- я пока не понял) находим в списке число, удаляем его и меняем пометки, смещаем при необходимости указатель на медиану (по ссылкам) и говорим её.
#include<algorithm>
#include<iostream>
using namespace std;
#define forr(i,x,y) for(int i=(int)(x); i<=(int)(y); i++)
int n,q[1000010],s[1000010],c[1000010],a[1000010],id[1000010];
bool cmp(int i,int j) { return bool(a[i]<a[j]); }
int sum(int i) { int ss=0;
while(i) { ss+=s[i]; i=i & (i-1); }
return ss;
}
int main() {
freopen("median.in","r",stdin); freopen("median.out","w",stdout);
scanf("%d",&n);
forr(i,0,n-1) { scanf("%d",&a[i]); id[i]=i; s[i+1]=0; c[i+1]=0; }
sort(id,id+n,cmp);
c[id[0]]=1; q[1]=a[id[0]];
forr(i,1,n-1) { c[id[i]]=c[id[i-1]]+(a[id[i]]!=a[id[i-1]]); q[c[id[i]]]=a[id[i]]; }
for(int i=0; i<=n-1; i++) {
int j=c[i],k=(i+2)/2,l=1,r=n;
while(j<=n) { ++s[j]; j=(j | (j-1))+1; }
while(r-l>1) if(sum((l+r)/2)>=k) r=(l+r)/2; else l=(l+r)/2;
if(sum(l)==k) printf("%d ",q[l]); else printf("%d ",q[r]);
}
}
Accept + (на SNWS-10 Day 2 Task #1).