Subset Sums
Xem dạng PDFBạn được cho một dãy số ~A_1~, ~A_2~, ..., ~A_n~ và ~m~ tập hợp ~S_1~, ~S_2~, ..., ~S_m~ các chỉ số của mảng này. Gọi ~S_k = \{S_{k, i}\}, 1 \leq i \leq |S_k|~. Nói cách khác, ~S_{k, i}~ là một phần tử bất kỳ từ tập hợp ~S_k~.
Trong bài này bạn sẽ phải trả lời ~q~ truy vấn có 2 dạng sau:
~?\ k~: Tính tổng ~\displaystyle \sum^{|S_k|}_{i = 1}{A_{S_{k, i}}}~, hay tổng các phần tử có vị trí thuộc tập ~S_k~ của dãy ~A~.
~+\ k\ x~: Cộng ~x~ vào các phần tử của dãy ~A~ có chỉ số trong tập ~S_k~. Phần tử ~A_{S_{k, i}}~ được thay bằng ~A_{S_{k, i}} + x~ với mọi ~i \in [1, |S_k|]~.
Với mỗi truy vấn loại đầu tiên hãy in tổng đã tính.
Input
Dòng đầu tiên gồm 3 số ~n, m, q~ (~1 \leq n, m, q \leq 10^5~). Dòng thứ hai gồm ~n~ phần tử ~A_1, A_2, \cdots, A_n~ (~|A_i| \leq 10^8~), các phần tử của dãy ~A~.
~m~ dòng tiếp theo, dòng thứ ~k~ gồm một số nguyên dương ở đầu cho biết số lượng phần tử của tập ~S_k~, theo sau bởi ~|S_k|~ số nguyên dương phân biệt ~S_{k, 1}, S_{k, 2}, \cdots, S_{k, |S_k|}~ (~1 \leq S_{k, i} \leq n~).
~q~ dòng tiếp theo, mỗi dòng có dạng ~?\ k~ hoặc ~+\ k\ x~. Với mọi truy vấn có ~1 \leq k \leq m~, ~|x| \leq 10^8~. Các truy vấn được cho theo thứ tự chúng cần được trả lời.
Đề đảm bảo tổng của kích thước mọi tập ~S_k~ không quá ~10^5~.
Output
Sau mỗi truy vấn dạng đầu tiên hãy in tổng đã tính trên một dòng.
Sample Input 1
5 3 5
5 -5 5 1 -4
2 1 2
4 2 1 4 5
2 2 5
? 2
+ 3 4
? 1
+ 2 1
? 2
Sample Output 1
-3
4
9
Notes
Ở truy vấn đầu tiên, tập ~S_k~ là ~S_2~ có 4 phần tử ~\{2, 1, 4, 5\}~. Tổng của 4 phần tử có vị trí trong tập ~S_2~ là ~A_2 + A_1 + A_4 + A_5 = -5 + 5 + 1 + (-4) = -3~.
Bình luận
Lời giải: Chia căn trên Tập hợp (Square Root Decomposition)
1. Phân tích bài toán
Bài toán yêu cầu thực hiện các thao tác cập nhật giá trị và truy vấn tổng trên các tập hợp phần tử của một mảng. Khó khăn nằm ở chỗ một phần tử có thể nằm trong rất nhiều tập hợp khác nhau.
Dữ kiện quan trọng: Tổng kích thước của tất cả các tập hợp không vượt quá 100,000. Đây là dấu hiệu để sử dụng kỹ thuật chia căn.
2. Ý tưởng chính
Chúng ta chia các tập hợp thành hai loại dựa trên số lượng phần tử của chúng (gọi ngưỡng chia căn là B, thường chọn B khoảng 320):
Vì tổng số phần tử của tất cả các tập là 100,000, nên số lượng tập nặng sẽ không bao giờ vượt quá 312 tập (100,000 chia cho 320). Số lượng tập nặng ít chính là chìa khóa để tối ưu.
3. Các thành phần lưu trữ và Tiền xử lý
Để giải bài này, ta cần các mảng sau:
Bước tiền xử lý:
4. Cách xử lý các truy vấn
Thao tác cập nhật (+ k x)
Đây là thao tác cộng giá trị x vào tất cả phần tử của tập hợp k.
Ta chỉ cần cộng x vào biến lazy_heavy của tập đó. Việc này cực nhanh.
Nếu k là tập nhẹ:
Thao tác truy vấn tổng (? k)
Đây là thao tác tính tổng các phần tử hiện có của tập hợp k.
Phần ảnh hưởng được tính bằng cách lấy: (Giá trị lazy của tập nặng khác) nhân với (Số phần tử chung giữa tập đó và tập k).
Nếu k là tập nhẹ:
5. Tại sao cách này hiệu quả?
6. Mã nguồn tham khảo (C++)
https://codeforces.com/problemset/problem/348/C
Bình luận này đã bị ẩn vì có quá nhiều phản ứng tiêu cực. Nhấn để xem.