Problem:
Problem J: The Incubator

サラリーマンの朝は早い。エリートコースを進み一流企業に入社して新人という肩書きからも卒業する頃、僕に下った辞令は未開の惑星における営業活動だった。辺鄙な土地での不便な生活を強いられているが、一応は左遷でなく栄転であり、その証拠に給料もぐんと上がっている - こんな場所では金なんて使いようもないのだけど。近年僕たちが直面している宇宙規模のエネルギー不足に対応するため、特定の生物種の個体から莫大なエネルギーを生成するテクノロジーが開発された。その特定の生物種というのが、この辺鄙な惑星の固有種なのだ。この生物が絶滅しないように保護しつつ、適度にエネルギーを回収していくのが僕の仕事だ。

エネルギーの回収は、いくつかのステップからなる。まずは、エネルギーの回収に使用する個体を選別する。個体によって得られるエネルギーの量は大きく異なるのだ。次に、選別された見込みのある個体に、インキュベーションという特別な処理を行う。インキュベートされた個体は膨大なエネルギーの源となる何かをを絶えず蓄えたり吐き出したりするので、個体にできるだけ多くのエネルギーの源となる何かが蓄えられている瞬間を狙って、円環の理に導く。するとお待ちかねのエネルギーが手に入る、という仕組みだ。

エリートサラリーマンに課せられるノルマは厳しい。しかし、僕にとって数十万のインキュベートされた個体を管理するのは朝飯前だ。今日は月末なので本社に月報を提出しなければならないが、今月はとても良い個体に遭遇したこともあって、過去最高の成績になりそうだ。

と喜んでいたのも束の間、最後の最後でひどいミスをやらかしてしまった。SQL文を打ち間違えて、今月の成果を記録しているデータベースのテーブル 1 つをまるごとふっ飛ばしてしまったのだ。あれがなければ、今月の成果は全く無しということになってしまう。降格、左遷、あるいは解雇もありえるかもしれない。

最後の頼みの綱は、作業のたびにこまめにつけていたログファイルだ。僕はいつも、個体をインキュベートするたびに一意な整数の番号を振り、インキュベートされた個体たちの番号を 1 つの配列に保存している。僕の営業活動は、次のような行動からなる。

個体をインキュベートし、その個体に番号 x を割り当て、その個体の番号を配列の末尾に追加する。

配列の n 番目の番号が示す個体を円環の理に導く。

番号 x の個体を円環の理に導く。

残念ながら僕は最大 lim 体の個体しか管理できない。個体をインキュベートしたとき、もしインキュベート済みの個体が lim を超えたならば、昔にインキュベートした個体から順に lim 以下になるまで円環の理に導く。

僕はこれら 4 つの営業活動を行うたびに、欠かさずログファイルに記入している。しかし、4 の活動だけはログファイルに一切記入していない。そうしても特に曖昧なところは残らないためだ。

いつも僕は、個体の番号の配列の操作を愚直に行なっている。しかし今度ばかりは、愚直に操作しながらログファイルを走査していては間に合いそうにない。月報の提出期限は 5 時間後に迫っている。

そこで、君たちにお願いがあるんだ。ログファイルから僕の営業活動を再現するプログラムを書いてほしい。もし書いてくれたら、お礼に君たちの願い事を何でも 1 つ叶えてあげよう。何だって構わない。どんな願いことだって叶えてあげられるよ。

Input

入力は複数のケースからなる。
各ケースは以下のフォーマットで与えられる。

ここには入力のフォーマットを書く。
q lim
query0 x0
.
.
.
queryq-1 xq-1

queryi が0の時、インキュベートした個体に xi の番号を割り当てたことを表す。

queryi が1の時、配列の xi 番目の番号が示す個体を円環の理に導く。

queryi が2の時、その時点で配列に含まれている中で　xi　番目の個体の番号を出力する

queryi が3の時、番号が xi の個体を円環の理に導く。

q = 0 かつ lim = 0の時入力の終わりを表す。

lim は32bit signed integerで表すことができる正の整数である。

すべてのクエリーについて、xi は0以上の整数で32bit signed integerで表すことができる。

0のクエリーについて、xi　は32bit signed integerの範囲に収まる非負整数で表される。

1,2のクエリーについて、xi の値は1以上の整数である。また存在しない配列の番号が指定されることはない

3のクエリーについて、存在しない個体番号が入力に含まれることはない。

また一度消去された個体の番号が値が同じテストケース内で、別の個体に割り当てられることはない。

ジャッジデータは次の2つのうち少なくとも片方を満たす。

1 ≤ q ≤ 400,000 かつテストケースの数が5個以下

1 ≤ q ≤ 10,000 かつテストケースの数は50個以下

Output

入力のクエリーが2の場合、x番目の個体番号を出力する

各ケースの最後には"end"を出力する

Sample input

22 5
0 0
0 1
0 2
0 3
0 4
2 1
2 2
2 3
2 4
2 5
0 5
2 1
0 6
2 2
3 3
2 2
2 2
1 2
2 2
2 1
2 2
2 3
30 5
0 383594529
1 1
0 868094164
0 708344471
0 4102559
0 944076771
0 320398558
1 1
0 949521499
0 1035499529
0 585547493
0 915496840
0 721553343
0 405934659
0 814301872
1 1
2 3
0 919753364
1 1
0 69231610
2 2
0 373477673
0 842917649
0 961543702
0 907959899
2 1
2 2
2 3
2 4
2 5
30 5
0 726736645
0 1
0 344304573
0 241734870
3 726736645
1 3
2 1
0 586879203
2 3
0 511883046
0 481344051
0 154183395
0 435126242
0 185906768
1 1
0 383123551
0 20253038
1 5
2 1
2 2
0 163044554
3 435126242
0 105612613
0 725050544
0 559442328
2 1
2 2
2 3
2 4
2 5
0 0

Sample output

0
1
2
3
4
1
3
4
4
5
2
5
6
end
405934659
405934659
69231610
373477673
842917649
961543702
907959899
end
1
586879203
154183395
435126242
383123551
163044554
105612613
725050544
559442328
end

The University of Aizu Programming Contest 2011 Summer

原案: Tomoya Sakai

問題文: Takashi Tayama

Solution in C++14 (judge verdict: Wrong Answer)

#include <algorithm>
#include <cassert>
#include <climits>
#include <cmath>
#include <iostream>
#include <unordered_map>
#include <queue>
#include <string>
#include <vector>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef vector<int> VI;
typedef vector<ll> VL;
typedef pair<int, int> ipair;
typedef tuple<int, int, int> ituple;

// const int INF = INT_MAX;
// const ll INF = LLONG_MAX;
// const int MOD = ((int)1e9 + 7);
// const ld EPS = (1e-10);
#define PI acosl(-1)
#define MAX_N (500000)

class BitSum{
protected:
  static const int max_value = 500000 + 2;
  ll bit[max_value + 1];

public:
  BitSum(int size) {
    assert(size < max_value);
    init();
  }
  void init(){
    for (int i = 0; i < max_value; i++){
      bit[i] = 0;
    }
  }
  void add(int idx, ll val){
    int p = idx+1;
    while(p < max_value){
      bit[p] += val;
      p += p & -p;
    }
  }
  /**
   * [0, idx]のsumを求める
   */
  ll calc(int idx){
    ll res = 0;
    int p = idx+1;
    while(0 < p){
      res += bit[p];
      p -= p & -p;
    }
    return res;
  }
  /**
   * [idx1, idx2]のsumを求める
   */
  ll range(int idx1, int idx2) {
    return calc(idx2) - calc(idx1 - 1);
  }
};

ll getNth(BitSum &bs, int index, int max_size){
  ll l = 1, h = max_size;
  ll prev = max_size;

  // printf("getNth; index = %d\n", index);

  while (l < h) {
    ll m = (l + h) / 2 + (l + h) % 2;
    ll res = bs.calc(m);
    // printf("l = %lld, h = %lld: calc(%lld) = %lld\n", l, h, m, res);
    if (res == index) {
      h = m;
    }
    else if (res < index) {
      l = m + 1;
    }
    else {
      h = m - 1;
    }

    if (abs(h - l) == prev) {
      break;
    }
    prev = abs(h - l);
  }

  return (l + h) / 2 + (l + h) % 2;
}

void exec(int q, int lim){
  unordered_map<int, int> number2index;
  unordered_map<int, int> index2number;
  VI incubated;
  BitSum bs = BitSum(MAX_N);
  int index;
  int count = 0;

  for (int i = 0; i < q; i++){
    int query, x;
    scanf("%d%d", &query, &x);
    // printf("query = (%d, %d)\n", query, x);

    switch (query) {
      case 0:
        incubated.push_back(x);
        bs.add(incubated.size(), 1);
        number2index[x] = incubated.size();
        index2number[incubated.size()] = x;
        count++;

        if (count > lim) {
          index = getNth(bs, 1, incubated.size());
          // printf("0: overflow. delete (%d, %d)\n", index, index2number[index]);
          bs.add(index, -1);
          count--;
        }
        break;
      case 1:
        index = getNth(bs, x, incubated.size());
        // printf("1: delete by index = (%d, %d)\n", index, index2number[index]);
        bs.add(index, -1);
        count--;
        break;
      case 2:
        index = getNth(bs, x, incubated.size());
        // printf("DEBUG: index = %d\n", index);
        printf("%d\n", index2number[index]);
        break;
      case 3:
        bs.add(number2index[x], -1);
        // printf("1: delete by number = (%d, %d)\n", x, number2index[x]);
        count--;
        break;
    }
  }

  printf("end\n");

}

void solve(){
  while (true){
    int q, lim;
    scanf("%d%d", &q, &lim);

    if (q + lim == 0) {
      break;
    }

    exec(q, lim);
  }
}

int main(){
  solve();
  return 0;
}