NKBRACKE - Dãy ngoặc đúng

Tags: segment-tree, data-structure

Problem

https://vn.spoj.com/problems/NKBRACKE

https://oj.vnoi.info/problem/NKBRACKE

Cho một xâu độ dài N chỉ gồm các kí tự (), các kí tự được đánh số từ 1 đến N theo chiều từ trái qua phải.

Một dãy ngoặc đúng được định nghĩa như sau:

  • Xâu rỗng là 1 dãy ngoặc đúng.
  • Nếu A là 1 dãy ngoặc đúng thì (A) là 1 dãy ngoặc đúng.
  • Nếu AB là 2 dãy ngoặc đúng thì AB là 1 dãy ngoặc đúng.

Cho M truy vấn, mỗi truy vấn thuộc 1 trong 2 loại sau:

  • 0 i ch: thay đổi kí tự ở vị trí i của xâu kí tự thành kí tự ch.
  • 1 i j: in ra 1 nếu xâu con từ vị trí i đến vị trí j là một dãy ngoặc đúng, in ra 0 trong trường hợp ngược lại.

Giới hạn

  • 2 <= N <= 100,000
  • 1 <= M <= 200,000

Trong truy vấn loại 1:  

  • 1 <= i <= N         
  • ch( hoặc )

Trong truy vấn loại 2:    

  • 1 <= i <= j <= N

Input

  • Dòng đầu tiên chứa 2 số N, M
  • Dòng tiếp theo chứa N kí tự liên tiếp.
  • M dòng tiếp theo, mỗi dòng chứa 1 truy vấn thuộc 1 trong 2 loại trên.

Output

  • In ra 0 hoặc 1 tương ứng với mỗi truy vấn loại 2.

Ví dụ

Input
8 7
()))(())
1 1 2
1 3 4
0 3 (
1 1 4
1 5 8
0 6 )
1 5 8

Output
10110 

Tutorial

Bài này sử dụng segment tree, với mỗi node lưu số lượng dấu ngoặc mở sai và dấu ngoặc đóng sai, tức các dấu chưa có cặp đi chung để triệt tiêu trong đoạn. Ví dụ chuỗi ngoặc )(()))()((()()(( sẽ được thu gọn thành ))((((, khi đó số ngoặc mở ( sai là 4 và số ) sai là 2. Ta chỉ cần những thông số này.

Khi merge hay combine 2 node con, ta có công thức sau:

# l, r là 2 node con
# lose là số lượng cặp ngoặc có thể triệt tiêu 
loss     = min(l.open, r.close)

open     = l.open - loss + r.open;
close    = r.close - loss + l.close;

Lấy ví dụ: các node con (sau khi đã loại bỏ các cặp triệt tiêu), ta có con trái là ))((( và con phải là ))(( thì số cặp được triệt tiêu là 2 (vì có 3 mở bên trái và 2 mở bên phải).


Submission

NKBRACKE.cpp