canuc80k7405·6/50

DSU Có Mốc Khôi Phục

Nộp bài trên LQDOJ

Bài 74 · Rollback

Ban đầu có đỉnh, mỗi đỉnh là một thành phần liên thông riêng. Xử lý lệnh:

  • : hợp thành phần chứa với thành phần chứa nếu chúng khác nhau, rồi in số thành phần hiện tại.
  • persist: đặt một mốc lưu trạng thái hiện tại. Các mốc được quản lý theo ngăn xếp.
  • rollback: khôi phục trạng thái tại mốc gần nhất, xóa mốc đó, rồi in số thành phần hiện tại.

Một lệnh giữa hai đỉnh vốn đã cùng thành phần vẫn là một lệnh hợp lệ và không làm đổi số thành phần.

Input

  • Dòng đầu chứa hai số nguyên và .
  • Mỗi trong dòng tiếp theo mô tả một lệnh theo một trong ba dạng trên.

Output

  • Sau mỗi lệnh và rollback, in số thành phần liên thông hiện tại trên một dòng riêng. Lệnh persist không tạo đầu ra.

Constraints

  • .
  • .
  • Mỗi lệnh rollback luôn có ít nhất một mốc chưa được dùng.

Scoring

  • Subtask ( số điểm): .
  • Subtask ( số điểm): Không có giới hạn bổ sung.

Example

Test 1

Input

5 8
union 1 2
persist
union 2 3
union 1 3
persist
union 4 5
rollback
rollback

Output

4
3
3
2
3
4

Giải thích: Phép hợp là dư thừa nên số thành phần vẫn là . Hai lần rollback lần lượt xóa hợp , rồi xóa cả các phép hợp sau mốc đầu, đưa số thành phần về và .

Lời giải tóm tắt
  • Phân tích: DSU thông thường dùng path compression, nhưng phép nén đường đi sửa nhiều parent khó hoàn tác. Ta chỉ dùng union-by-size, vì thế mỗi union thành công đổi đúng một parent, một size và biến đếm thành phần.
  • Thuật toán: Với union thành công, gắn gốc nhỏ vào gốc lớn và lưu (child, oldSizeOfParent); với union dư thừa, lưu một sentinel để lịch sử vẫn biểu diễn đúng một lần gọi union. persist lưu history.size(). rollback pop đến kích thước đã lưu, đảo từng union thành công và tăng lại số thành phần.
  • Chứng minh: Không path compression nên ngoài các trường được ghi, DSU không bị sửa. Mỗi bản ghi thành công chứa đủ dữ liệu để trả gốc con về chính nó và size gốc cha về giá trị cũ; sentinel không cần đổi gì. Do rollback xử lý ngược thứ tự tới đúng snapshot, trạng thái parent, size và số thành phần được khôi phục chính xác.
  • Độ phức tạp: Với union-by-size, find mất O(log n) trong trường hợp xấu nhất; tổng bộ nhớ O(n+q).