Nguồn đề bài: http://vn.spoj.com/problems/ORDERSET/
1. Đề bài ORDERSET spoj
Tập hợp thứ tự
Bạn cần quản lý một tập hợp động các số, hỗ trợ hai thao tác cơ bản:
- INSERT(S,x): nếu x không thuộc S, thêm x vào S
- DELETE(S,x): nếu x thuộc S, xóa x khỏi S
và hai loại truy vấn
- K-TH(S) : trả về số bé thứ k của S
- COUNT(S,x): đếm số lượng số thuộc S bé hơn x
Dữ liệu
- Dòng 1: Q (1 ≤ Q ≤ 200000), số thao tác
- Q dòng sau, đầu mỗi dòng chứa ký tự I, D, K hoặc C cho biết thao tác tương ứng là INSERT, DELETE, K-TH hay COUNT. Tiếp theo là một khoảng trắng và một số nguyên là tham số cho thao tác đó.
Nếu tham số là giá trị x, dữ liệu bảo đảm 0 ≤ |x| ≤ 109. Nếu tham số là chỉ số k, dữ liệu bảo đảm 1 ≤ k ≤ 109.
Kết quả
Với mỗi truy vấn, in ra kết quả tương ứng trên một dòng. Với truy vấn K-TH, nếu k lớn hơn số phần tử của S, in ra ‘invalid’.
Ví dụ
Dữ liệu 8 I -1 I -1 I 2 C 0 K 2 D -1 K 1 K 2 Kết quả 1 2 2 invalid
2. Hướng dẫn ORDERSET spoj
Dùng segment tree.
Khởi tạo toàn bộ cây có giá trị bằng 0.
Tại mỗi lá, nếu lá đó trong S có giá trị bằng thứ tự của lá đó, ta sẽ cập nhập lá đó bằng 1, và cập nhật lại toàn bộ cây.
Khi nhập vào lệnh INSERT(S,x) , ta cập nhật lại cây.
r=pos[b[i]]; it[r]=1; while(r/2>0) { r/=2; it[r]=it[2*r] + it[2*r+1]; }
Trong đó, pos là vị trí của lá có giá trị là b[i].
Khi nhập vào lệnh DELETE(S,x), ta cập nhật lại cây tương tự:
r=pos[b[i]]; it[r]=0; while(r/2>0) { r/=2; it[r]=it[2*r] + it[2*r+1]; }
Khi nhập vào lệnh K-TH(S), cần phải trả về số bé thứ k, ta sử dụng chặt nhị phân để tìm:
lo=0, hi=q; while(hi-lo>1) { mid=(lo+hi)/2; if(a[i]<=get(1,1,q,1,mid)) hi=mid; else lo=mid; }
Bởi vì có thể số phần tử trong S ít hơn k nên ta cần phải thêm vào
if(a[i]>get(1,1,q,1,q)) { cout<<"invalid"<<"n"; continue; }
Khi nhập vào lệnh COUNT(S,x) – đếm số lượng số thuộc S bé hơn x, ta tính tổng tất cả các lá từ 1 tới x-1
Đó chính là kết quả.
* Bởi vì |x|<=10^9 nên không thể tạo 1 cây it với từng đấy lá.
Do Q<=200000 nên ta sử dụng phương pháp nén dữ liệu.
3. Code ORDERSET spoj C++
#include <bits/stdc++.h> using namespace std; int q, x; char s[200001]; int a[200001], b[200001], pos[200001], c[200001]; int it[1000001]; void khoitao(int r, int k, int l) { if(k==l){pos[k]=r; it[r]=0; return;} int g=(k+l)/2; khoitao(2*r,k,g); khoitao(2*r+1,g+1,l); it[r]=it[2*r]+it[2*r+1]; it[r]=0; } int get(int r, int k, int l, int u, int v) { if(v<k || u>l) return 0; if(u<=k && l<=v) return it[r]; int g=(k+l)/2; int t1=get(2*r,k,g,u,v); int t2=get(2*r+1, g+1, l, u,v); return t1+t2; } int main() { cin>>q; for(int i=1; i<=q; i++) { cin>>s[i]>>a[i]; c[i]=a[i]; } sort(c+1,c+q+1); for(int i=1; i<=q; i++) b[i]=lower_bound(c+1,c+q+1,a[i])-c; khoitao(1,1,q); for(int i=1; i<=q; i++) { if(s[i]=='I') { int r=pos[b[i]]; it[r]=1; while(r/2>0) { r/=2; it[r]=it[2*r] + it[2*r+1]; } }else if(s[i]=='D') { int r=pos[b[i]]; it[r]=0; while(r/2>0) { r/=2; it[r]=it[2*r] + it[2*r+1]; } } else if(s[i]=='K') { if(a[i]>get(1,1,q,1,q)) { cout<<"invalid"<<"n"; continue; } int lo=0, hi=q; while(hi-lo>1) { int mid=(lo+hi)/2; if(a[i]<=get(1,1,q,1,mid)) hi=mid; else lo=mid; } cout<<c[hi]<<"n"; } else cout<<get(1,1,q,1,b[i]-1)<<"n"; } }
[/sociallocker]