반응형
일반적으로 STL의 std::set은 k-th element를 찾거나 counting을 log 시간에 할 수 없다.
PBDS(Policy Based Data Structure)은 이를 해결할 수 있다고 한다.
Set과 같은 정렬 형태의 자료구조가 필요하면서 위의 동작 요구하는 BOJ의 일반적인 문제는 입력의 제한이 있기 때문에 세그먼트 트리 등으로 충분히 해결할 수 있는 경우가 대부분이다.
다만, interaction 문제나 세그먼트 트리로 만들어야하는 key 값의 range가 매우 크거나 실시간 쿼리가 필요한 경우 PBDS를 통해 해결하는 것이 조금 더 효율적일 것이다.
사실 BBST(Balanced Binary Search Tree)라면 PBDS를 쉽게 구현할 수 있는 것 같아서 유명한 RB Tree(Red-Black Tree)를 활용하여 구현하였다.
* PBDS의 실제 코드를 참고하지는 않아서 최적화나 정확성이 떨어지는 코드일 수 있다.
IsK(int k) : key에 맞는 Node 포이터를 구할 수 있다
IsN(int n) : k-th element 값을 구할 수 있다
key value가 int형이 아니라면 Node 구조체에서 수정하고 비교연산자 오버라이딩을 하면 될 것 같다
아래는 BOJ 2243번을 해결하는 소스코드이다.
더보기
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long double ld;
typedef long long ll;
typedef __int128_t lll;
typedef pair<ll, ll> pll;
enum Color{Red,Black};
struct Node{
int k=0; // key
int n=0, ln=0, rn=0; // num
Node *p=nullptr, *l=nullptr, *r=nullptr; // parent, left, right
Color c = Black; // color
};
class RBTree{
public: // private
int n;
Node *root, *nil;
pair<Node*, Node*> IsK(int k){
Node *t = root, *p = nullptr;
for(; t!=nil && t->k != k; p = t, t = (k < p->k) ? p->l : p->r);
return {t, p};
}
int IsN(int n){
for(Node *t = root; t != nil;){
if(n <= t->ln) t=t->l;
else if(n <= t->ln+t->n){
int ret = t->k;
Delete(t->k, 1);
return ret;
}
else n -= t->ln+t->n, t=t->r;
}
}
void Insert(int k, int n){
Node *z;
auto[x,y] = IsK(k);
if(x == nil) { // no key k
z = new Node{k,n,0,0,y,nil,nil,Red};
if(y == nullptr) root = z, z->c = Black;
else {
if(z->k > y->k) y->r = z;
else y->l = z;
if(y->p != nullptr) InsertFix(z);
}
}
else z = x, z->n += n;
for(Node *p = z->p; p!=nullptr; z=p, p=p->p) (p->l == z) ? p->ln += n : p->rn += n;
return;
}
void InsertFix(Node *z){
while(z!=root && z->p->c == Red){
Node *q = z->p->p; // grand parent
Node *u = (z->p == q->l) ? q->r : q->l; // uncle
bool s = (z->p == q->l);
if(u && u->c == Red){
z->p->c = u->c = Black, q->c = Red;
z = q;
}
else{
if(z==(s?z->p->r:z->p->l)) z=z->p, s?RotL(z):RotR(z);
z->p->c = Black, q->c = Red;
s?RotR(q):RotL(q);
}
}
root->c = Black;
}
void Delete(int k, int n){
auto[x,y] = IsK(k);
if(x==nil) return;
Node *z = x;
for(z->n -= n; y!=nullptr; x=y, y=x->p) (y->l == x) ? y->ln -= n : y->rn -= n;
if(!z->n){
Color c = z->c;
if(z->l == nil) x = z->r, Trans(z, z->r);
else if(z->r == nil) x = z->l, Trans(z, z->l);
else{
y = tree_minimum(z->r);
c = y->c;
x = y->r;
for(Node *t=y; t->p!=z; t=t->p) (t->p->l == t) ? t->p->ln -= y->n : t->p->rn -= y->n;
if(y->p == z) x->p = y;
else{
Trans(y, y->r);
y->r = z->r;
y->r->p = y;
}
Trans(z, y);
y->l = z->l;
y->l->p = y;
y->ln = y->l->ln + y->l->n + y->l->rn;
y->rn = y->r->ln + y->r->n + y->r->rn;
y->c = z->c;
}
delete z;
if(c == Black) DeleteFix(x);
}
}
void DeleteFix(Node *z){
for(Node *s; z != root && z->c == Black;){
int t = (z == z->p->l);
s = t?z->p->r:z->p->l;
if(s->c == Red){
s->c = Black;
z->p->c = Red;
t?RotL(z->p):RotR(z->p);
s = t?z->p->r:z->p->l;
}
if(s->l->c == Black && s->r->c == Black){
s->c = Red;
z = z->p;
}
else{
if((t?s->r->c:s->l->c) == Black){
t ? s->l->c = Black : s->r->c = Black;
s->c = Red;
t?RotR(s):RotL(s);
s = t?z->p->r:z->p->l;
}
s->c = z->p->c;
z->p->c = Black;
t?s->r->c = Black : s->l->c = Black;
t?RotL(z->p):RotR(z->p);
z = root;
}
}
z->c = root->c = Black;
}
void Trans(Node *x, Node *y){ (x->p == nullptr) ? root = y : (x == x->p->l) ? x->p->l = y : x->p->r = y; y->p = x->p; }
void RotL(Node *z){
Node *y = z->r;
z->r = y->l;
if(y->l != nil) y->l->p = z;
y->p = z->p;
if(!z->p) root = y;
else if(z == z->p->l) z->p->l = y;
else z->p->r = y;
z->p = y;
y->l = z;
z->rn = z->r->ln + z->r->n + z->r->rn;
y->ln = z->ln + z->n + z->rn;
}
void RotR(Node *z){
Node *x = z->l;
z->l = x->r;
if(x->r != nil) x->r->p = z;
x->p = z->p;
if(!z->p) root = x;
else if(z == z->p->l) z->p->l = x;
else z->p->r = x;
z->p = x;
x->r = z;
z->ln = z->l->ln + z->l->n + z->l->rn;
x->rn = z->ln + z->n + z->rn;
}
public:
RBTree() { nil = new Node{0,0,0,0,nullptr,nullptr,nullptr,Black}, root = nil; }
Node* tree_minimum(Node* t){ for(;t->l != nil;t=t->l); return t; }
Node* tree_maximum(Node* t){ for(;t->r != nil;t=t->r); return t; }
void Inorder(Node *t) {
if (t == nil) return;
Inorder(t->l);
std::cout << "(" << t->k << "(" << t->ln << "," << t->n << "," << t->rn << ")," << (t->c == Black ? "B" : "R") << ")";
Inorder(t->r);
}
};
int main() {
ios::sync_with_stdio(0), cin.tie(0);
RBTree T;
int n;
for(cin >> n;n--;){
int t, x, y; cin >> t;
if(t==1) cin >> x, cout << T.IsN(x) << endl;
else cin >> x >> y, y>0 ? T.Insert(x, y) : T.Delete(x,-y);
//T.Inorder(T.root), cout << endl;
}
}
반응형
'PS > Study' 카테고리의 다른 글
공부할 알고리즘 목록 (0) | 2021.11.03 |
---|---|
알고리즘별 기본 문제 (수정 예정) (0) | 2021.08.19 |
카탈란 수 ( Catalan Number ) (0) | 2021.08.18 |
모듈러 인버스 (modulo inverse) (0) | 2021.08.09 |
에라토스테네스의 체 (0) | 2021.08.09 |