AOJ : 1083 - The Incubator
問題概要
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1083
日本語の問題文なので, 説明は省略です.
アルゴリズム
セグメント木を使うことで解くことができます.
解くときにメモったのをそのまま貼りつけただけ↓
セグメント木
- 配列のxi番目を処理したいときに使用
- dat[] : セグメント木のデータ
- 各ノードは, int型で範囲の要素数を管理
- size : dat配列のリーフの何番目まで埋まってるか
- low以上の値とする
- 要素の追加処理 add(int x)
- xの番号の個体のノードを配列の最後へ追加する処理
- size++;
- lim--;
- 要素の検索処理 find(x)
- 配列x番目のノードがdat配列の何番目に入っているかを求める
- 要素の削除処理 erase(int pos)
- dat[pos]からルートまでのぼりながら, 各ノードをデクリメント
- lim++;
マップ map
- 個体番号x番目を処理したいときに使用
- key : 個体番号
- val : セグメント木のdat配列の何番目にデータ格納したか
- 個体番号xi番目を追加したときは, map[xi] = size;
- 個体番号xi番目を削除したときは, 特に何もする必要なし
プログラム
typedef pair<int,int> P; int dat[1<<20+2]; //セグメント木 int low = 1<<19-1; //リーフノードの最小インデックス int q,lim; int size; map<int,int> num; int rnum[400000]; //個体番号xのノードを追加する処理 void add(int x){ num[x] = size; dat[size] = 0; rnum[size-low] = x; lim--; size++; int pos = size - 1; //ルートノードまで登りながら, 各ノードをインクリメント while(0 < pos){ dat[pos]++; pos = (pos - 1) / 2; } } //配列のx番目が, dat配列の何番目か求める int find(int x){ int pos = 0; //左にもぐるか, 右にもぐるか決めつつ, リーフまでもぐる while(pos < low){ int left = pos * 2 + 1; int right = pos * 2 + 2; if(x <= dat[left]){ pos = left; } else{ x -= dat[left]; pos = right; } } return pos; } //dat配列のpos番目を削除する処理 void erase(int pos){ lim++; while(0 < pos){ dat[pos]--; pos = (pos - 1) / 2; } } int main(void){ while(scanf("%d%d",&q,&lim),q||lim){ memset(dat,0,sizeof(dat)); memset(rnum,0,sizeof(rnum)); num.clear(); size = low; while(q--){ int query,x; scanf("%d%d",&query,&x); if(query == 0){ add(x); if(lim < 0){ //もし, 個体の数がlimを超えたら, 配列の0番目を削除 int pos = find(0); erase(pos); } } else if(query == 1){ int pos = find(x); erase(pos); } else if(query == 2){ int pos = find(x); printf("%d\n",rnum[pos-low]); } else if(query == 3){ int pos = num[x]; erase(pos); } } printf("end\n"); } }