http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3612
题意:对一集合有addx和removex两种操作。输出每次操作后该集合的中位数。
解法:用set来维护这个集合,同时用一指针指向每个时刻中位数。注意multiset每次查找后迭代器指向相同元素的第一个而且删除迭代器指向的元素后迭代器就不知道指向哪儿了。所以要记得保存迭代器信息,和数组不一样不会下一个元素不会前移。
add和remove元素时,对于集合元素个数的奇偶性和要操作的元素和当前所指元素的大小关系可以方便得到指针的移动规律。
这种做法比线段树做法在查找时提高了效率,从O(logn)降到了O(1):
时间790ms效果明显。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const double eps=1e-7;
const double INF=1e50;
const double pi=acos(-1);
#define N 15
int main()
{
//freopen("a","r",stdin);
int i,j,b,t,n,as;
char s[10];
multiset<int> a;
scanf("%d",&t);
for (int kk=1;kk<=t;kk++)
{
a.clear();
scanf("%d",&n);
multiset<int>::iterator it;
multiset<int>::iterator it1;
multiset<int>::iterator it2;
for (i=1;i<=n;i++)
{
scanf("%s %d",s,&b);
if (s[0]=='a')
{
a.insert(b);
if (a.size()==1) it1=a.begin();
else
{
if ((a.size()%2-1)!=0 && b<*it1) it1--;
if ((a.size()%2-1)==0 && b>=*it1) it1++;
}
}
else if (s[0]=='r')
{
it=a.find(b);
if (it==a.end())
{
printf("Wrong!\n");
continue;
}
else
{
if (*it1==b)
{
it=it1;//就删所指向的那个元素,比较方便
if (a.size()%2==0) it1++;
else it1--;
}
else
{
if (a.size()%2!=0 && b>*it1) it1--;
if (a.size()%2==0 && b<*it1) it1++;
}
a.erase(it);//先复制信息后操作
}
}
j=a.size();
if (j==0) printf("Empty!\n");
else
{
if (j%2!=0) printf("%d\n",*it1);
else
{
it2=it1;
it2++;
long long j2=(long long)*it1+(long long)*it2;
if (j2%2==0) printf("%lld\n",j2/2);
else if (j2==-1) printf("-0.5\n");
else printf("%lld.5\n",j2/2);
}
}
}
}
return 0;
}