順序付けされていない配列のM番目に小さい数値をクエリし、クエリ中に継続的に挿入および削除すると、クエリが大きすぎるたびに並べ替えるコストが発生します。
可能な最大値maxnを範囲とし、数値をceil(sqrt(maxn))グループ、各グループのfloor(sqrt(maxn))の数に分割し、2つの配列を設定します。1つはそれぞれの回数をカウントします。番号が表示され、他は各グループの番号の数を数えます。
クエリを実行するときは、最初にどのブロックにあるかを判別してから、そのブロックでM番目のブロックを見つけます。
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;
const int maxn = 100001;
const int blk = 317;
const int blk_size = 316;
int N;
stack<int> s;
int num_count[maxn]={
}; //每个数的个数
int blk_count[blk]={
}; //每个块中数的个数
int findMedian();
int main(){
scanf("%d", &N);
while(N--){
char command[15];
scanf("%s", command);
if(strcmp(command, "Push")==0){
int v;
scanf("%d", &v);
s.push(v);
num_count[v]++;
blk_count[v/blk_size]++;
}
else if(strcmp(command, "Pop")==0){
if(s.empty()) printf("Invalid\n");
else{
int v = s.top();
printf("%d\n", v);
s.pop();
num_count[v]--;
blk_count[v/blk_size]--;
}
}
else{
if(s.empty()) printf("Invalid\n");
else printf("%d\n", findMedian());
}
}
return 0;
}
int findMedian(){
int n = s.size();
int M = (n%2==0)?(n/2):((n+1)/2);
int sum = 0;
for(int b=0; b<blk; b++){
if(sum+blk_count[b]<M){
sum += blk_count[b];
continue;
}
else{
for(int i=b*blk_size; i<(b+1)*blk_size; i++){
if(sum+num_count[i]<M){
sum += num_count[i];
continue;
}
else return i;
}
}
}
}