CHUYÊN đề cấu TRÚC dữ LIỆU NÂNG CAO

28 31 0
CHUYÊN đề cấu TRÚC dữ LIỆU NÂNG CAO

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Interval Tree là công cụ rất hữu dụng được sử dụng nhiều trong các bài toán trên dãy số, hoặc được quy về các bài toán xử lí trên dãy số, đặc biệt là các bài toán có nhiều công việc cần xử lí và nhiều truy vấn xen kẽ nhau. Phần lí thuyết về Interval Tree đã được trình bày rất rõ ràng ở nhiều tài liệu do các chuyên gia, đồng nghiệp dạy bồi dưỡng học sinh giỏi chia sẻ, nên tôi mạn phép không đề cập tại đây. Do năng lực có hạn nên tôi không viết hoặc nghĩ ra những bài tập mới mà có sử dụng Interval Tree để giải được. Vì thế, trong chuyên đề này thực chất là các bài tập tôi sưu tầm, biên tập thành tập tài liệu để phục vụ trong công tác giảng dạy bồi dưỡng HSG môn Tin học. Ở đây, tôi trích dẫn các bài tập nguồn từ SPOJ, Codeforce và nhiều nguồn khác. Với mỗi bài tập tôi đề cập đến ba vấn đề: • Tóm tắt đề bài rõ ràng • Thuật toán tốt • Code demo (nếu có). Khi áp dụng tài liệu này vào giảng dạy, tôi thường bỏ phần “code demo” để không “làm hỏng học sinh”, chỉ phát đề cho học sinh. Với mỗi bài tập, sau khi học sinh nghiên cứu và đề xuất ý tưởng (hoặc code nộp mà chưa AC), tôi dẫn dắt, đưa ra giải thuật của bài toán đó để học sinh “ngấm” bài toán hơn. Dần dần học sinh nắm được tư tưởng Interval Tree và ứng dụng linh động vào các bài toán khác.

CHUYÊN ĐỀ: CẤU TRÚC DỮ LIỆU NÂNG CAO Interval Tree công cụ hữu dụng sử dụng nhiều toán dãy số, quy tốn xử lí dãy số, đặc biệt tốn có nhiều cơng việc cần xử lí nhiều truy vấn xen kẽ Phần lí thuyết Interval Tree trình bày rõ ràng nhiều tài liệu chuyên gia, đồng nghiệp dạy bồi dưỡng học sinh giỏi chia sẻ, nên mạn phép không đề cập Do lực có hạn nên tơi khơng viết nghĩ tập mà có sử dụng Interval Tree để giải Vì thế, chuyên đề thực chất tập sưu tầm, biên tập thành tập tài liệu để phục vụ công tác giảng dạy bồi dưỡng HSG môn Tin học Ở đây, tơi trích dẫn tập nguồn từ SPOJ, Codeforce nhiều nguồn khác Với tập đề cập đến ba vấn đề: • Tóm tắt đề rõ ràng • Thuật tốn tốt • Code demo (nếu có) Khi áp dụng tài liệu vào giảng dạy, thường bỏ phần “code demo” để không “làm hỏng học sinh”, phát đề cho học sinh Với tập, sau học sinh nghiên cứu đề xuất ý tưởng (hoặc code nộp mà chưa AC), tơi dẫn dắt, đưa giải thuật tốn để học sinh “ngấm” tốn Dần dần học sinh nắm tư tưởng Interval Tree ứng dụng linh động vào tốn khác Tơi xin trích dẫn tài liệu tơi tham khảo để biên tập thành chuyên đề này: • • • • • http://laptrinh.ntu.edu.vn/Problem/List http://codeforces.com/problemset http://vn.spoj.com/problems/oi/ https://onlylove97.wordpress.com/category/it/ https://doraemonvodanh.wordpress.com/category/thuat-toan/segment-tree/bai- tap-interval-tree/ • Quyển: Một số vấn đề ý mơn Tin học – Nhóm tác giả Đại học Vinh Ứng dụng Interval Tree để giải toán sau: Bài Phần tử thứ K http://vn.spoj.com/problems/YPKTH/ Cho dãy số A có N phần tử nguyên phân biệt Cho Q truy vấn, truy vấn có dạng: L R K Yêu cầu: truy vấn xuất phần tử lớn thứ K sau xếp phần tử AL, AL+1, …, AR theo thứ tự tăng dần Giới hạn: ≤ N, Q ≤ 105 |Ai| ≤ 109 với ≤ i ≤ N 1≤L≤R≤N ≤ K ≤ R-L+1 Input: - Dòng chứa số N - Dòng chứa N số A1, A2, …, AN - Dòng chứa số Q - Q dòng tiếp theo, dòng chứa số L, R, K Output: Q dòng, dòng chứa câu trả lời cho truy vấn theo thứ tự nhập vào Ví dụ: Input Output 2154368 4 122 374 462 Thời gian chạy: 551 1s-3s THUẬT TOÁN : Dùng Segment Tree với nút lưu lại dãy từ l->r sort Dùng vector cho nút để giảm nhớ: nút xuất logN lần cây, nhớ NlogN Có thể tạo O(NlogN), lần hợp hai nút lại ta trộn hai đoạn O(n+m) với n, m kích thước hai đoạn Với truy vấn ta làm sau: Xét giá trị (gọi res) có dãy cách chặt nhị phân, (nút thực chất sort dãy tăng dần nên chặt nhị phân nút 1), đếm xem đoạn l r có phần tử nhỏ nó, nhỏ k tức phải tìm số lớn tương tự Với lần truy vấn l r ta lại chặt nhị phân nút nằm đoạn l r để tìm phần tử lớn ≤ res đồng thời kiểm tra xem res có mặt đếm số lượng phần tử nhỏ res (Chú ý phần tử phân biệt) Điều kiện để res nghiệm cnt == k-1 (cnt số lượng số < res) tìm thấy res đoạn l r Code demo: http://ideone.com/GTScHq #include using namespace std; typedef long long ll; typedef int64_t ll; typedef pair ii; #define EL printf("\n") #define pb push_back #define mp make_pair #define X first #define Y second typedef vector data; const int N = 100100; int n, q, a[N], L, R, k, res, cnt, f; data t[4*N], nil; data combine(data u, data v) { data ans = nil; int i = 0, j = 0; while (i < u.size() and j < v.size()) { if (u[i] < v[j]) ans.pb(u[i++]); else ans.pb(v[j++]); } while (i < u.size()) ans.pb(u[i++]); void get(int node, int l, int r) { if (r < L or R < l) return ; if (L ≤ l and r ≤ R) { int i = 0, j = t[node].size()-1, pos = -1; while (i ≤ j) { int mid = (i+j)/2; if (t[node][mid] ≤ res) { pos = mid; i = mid+1; } else j = mid-1; } if (pos == -1) return ; if (t[node][pos] == res) f = true; cnt += pos + 1; if (t[node][pos] == res) cnt ; return ; } int mid = (l+r)/2; get(node*2,l,mid); get(node*2+1,mid+1,r); } while (j < v.size()) ans.pb(v[j++]); return ans; } void build(int k, int l, int r) { if (l == r) { t[k].pb(a[l]); return ; } int mid = (l+r)/2; build(k*2, l, mid); build(k*2+1, mid+1, r); t[k] = combine(t[k*2], t[k*2+1]); } int main() { //freopen("YPKTH.INP","r",stdin); //freopen("YPKTH.OUT","w",stdout); scanf("%d", &n); for (int i=1;i≤n;i++) scanf("%d", &a[i]); build(1,1,n); scanf("%d", &q); while (q ) { scanf("%d%d%d", &L,&R,&k); int l = 0, r = t[1].size()-1; while (l ≤ r) { int mid = (l+r)/2; res = t[1][mid]; cnt = 0; f = 0; get(1,1,n); if (cnt == k-1 and f) { printf("%d\n", res); break; } if (cnt < k) l = mid+1; else r = mid-1; } } return 0; } Bài Đoạn có tổng lớn http://vn.spoj.com/problems/GSS/ Cho dãy số a[1], a[2], , a[n] (|a[i]| ≤ 15000, n ≤ 50000) Hàm q(x, y) = max { tổng(a[i]+a[i+1]+ +a[j]), x ≤ i ≤ j ≤y } Cho m câu hỏi dạng x, y (1 ≤ x ≤ y ≤ n), (m ≤ 50000) -> tính q(x, y) Input - Dòng đầu n - Dòng thứ hai dãy a - Dòng thứ m - m dòng dòng cặp số x, y Output Lần lượt ghi q(x, y) tương ứng Mỗi kết ghi dòng Example Input: -1 12 Output: Thời gian chạy: Thuật toán: Sử dụng Segment Tree Một nút lưu giá trị : sum : tổng đoạn pre : tổng lớn đoạn tiền tố suf : tổng lớn đoạn hậu tố ans : tổng lớn đoạn Công thức hợp hai nút sau : res.sum = l.sum + r.sum; res.pre = max(l.pre, l.sum + r.pre); res.suf = max(r.suf, r.sum + l.suf); res.ans = max(l.ans, r.ans, l.suf + r.pre); Code demo: #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include data make_data (int x) { data res; res.sum = x; res.pre = res.suf = res.ans = x; return res; } void make_tree(int k, int l, int r) { if (l == r) { t[k] = make_data(a[l]); return ; } int mid = (l+r)/2; make_tree(k*2, l, mid); make_tree(k*2+1, mid+1, r); using namespace std; typedef long long ll; typedef int64_t ll; typedef double real; const int const int const ll const real base = 1000000007; oo = INT_MAX; ooo = LONG_LONG_MAX; pi = acos(-1.0); t[k] = combine(t[k*2], t[k*2+1]); } data query(int k, int l, int r, int L, int R) { if (l == L and r == R) return t[k]; int mid = (l+r)/2; if (R ≤ mid) return query(k*2, l, mid, L, R); #define openf {freopen("INP.INP","r",stdin);freopen("OU T.OUT","w",stdout);} #define closef {fclose(stdin);fclose(stdout);} #define readln scanf("\n") #define writeln printf("\n") if (L > mid) return query(k*2+1, mid+1, r, L, R); return combine ( query(k*2, l, mid, L, mid), query(k*2+1, mid+1, r, mid+1, R) ); // -/ / struct data { int sum, pre, suf, ans; }; const int maxn = 100000, maxt = 4*maxn; int n, a[maxn], m, L, R; } int main() { //openf; scanf("%d", &n); data t[maxt]; data combine (data l, data r) { data res; res.sum = l.sum + r.sum; res.pre = max(l.pre, l.sum + r.pre); res.suf = max(r.suf, r.sum + l.suf); res.ans = max( max(l.ans, r.ans), l.suf + r.pre); return res; } for (int i=1;i≤n;i++) scanf("%d", &a[i]); make_tree(1,1,n); scanf("%d", &m); while (m ) { scanf("%d%d",&L,&R); printf("%d\n",query(1,1,n,L,R).ans); } //closef; return 0; } Bài Diện tích hình chữ nhật - http://vn.spoj.com/problems/AREA/ Trên mặt phẳng toạ độ người ta vẽ N hình chữ nhật Hãy tính diện tích che phủ N hình chữ nhật này, biết N hình chữ nhật song song với trục Ox Oy Input Dòng : Số nguyên N ( ≤ N ≤ 10000 ) N dòng tiếp theo, dòng gồm số nguyên x1, y1, x2, y2 tương ứng toạ độ góc trái góc phải hình chữ nhật thứ i.( ≤ x1 ≤ x2 ≤ 30000, ≤ y1 ≤ y2 ≤ 30000 ) Output Gồm dịng ghi diện tích phủ N hình chữ nhật Example Input: 10 10 20 20 15 15 25 30 Output: 225 Thời gian chạy: Thuật toán: Sử dụng Segment Tree (IT) Chuyển liệu đề cho sang dãy tọa độ x, x có lưu lại y1 y2 tương ứng hàng giới hạn trên, đồng thời lưu lại type -1 hay tương ứng cạnh đóng hay mở Sau sort lại mảng theo x Mục đích cách xử lí khoảng từ xi -> xi+1 xét dãy sort ta tính phần diện tích bao phủ y1 y2 Lúc ta dùng Segment Tree, nút lưu lại cnt số lượng đoạn phủ len tổng chiều dài Code demo: http://ideone.com/tGGNUI #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef long long typedef int64_t typedef double ll; ll; real; // -// struct Node { int x, y1, y2, type; }; struct Tree { int len, cnt; }; // -// void update(int k, int l, int r, int L, int R, int type) { if (r < L or R < l) return ; if (L ≤ l and r ≤ R) { t[k].cnt += type; if (type == 1) // them hcn nen bao phu ca canh t[k].len = (r-l+1); // chang han l = 5, r = thi t[k].len = (5,6,7,8) else { // truong hop xoa thi phai lay thong tin tu node if (t[k].cnt == 0) t[k].len = t[k*2].len + t[k*2+1].len; } return ; } int mid = (l+r)/2; update(k*2,l,mid,L,R,type); update(k*2+1,mid+1,r,L,R,type); if (t[k].cnt == 0) t[k].len = t[k*2].len + t[k*2+1].len; } int main() { //freopen("INP.INP","r",stdin); //freopen("OUT.OUT","w",stdout); scanf("%d", &n); for (int i=1;i≤n;i++) { int x1, y1, x2, y2; scanf("%d%d%d%d", &x1,&y1,&x2,&y2); const int N = 30010; int n, x0; // x0 la vi tri cuoi cung dang xet ll res; Node a[N]; Tree t[5*N]; // luu ve phuong dien dai, khong phai toa (*) // // a[i].x = x1; a[i+n].x = x2; a[i].y1 = a[i+n].y1 = y1; a[i].y2 = a[i+n].y2 = y2; a[i].type = 1; a[i+n].type = -1; } n *= 2; sort(a+1,a+n+1,cmp); bool cmp(const Node u, const Node v) { //for (int i=1;i≤n;i++) return (u.x < v.x or (u.x == v.x and u.type // printf("%d %d %d %d\n", a[i].x, a[i].y1, < v.type)); a[i].y2, a[i].type); } for (int i=1;i≤n;i++) { //cout

Ngày đăng: 09/03/2021, 13:57

Mục lục

    Thuật toán: Sử dụng Segment Tree

    Một nút lưu các giá trị :

    Cho q câu hỏi, mỗi câu có dạng (u, v): Cho biết phần tử có giá trị lớn nhất thuộc đoạn [u, v]

    +) biến đổi có dạng: 0 x y value

Tài liệu cùng người dùng

Tài liệu liên quan