using System;
using System.Collections.Generic;
class GfG {
// Declaring Structure for storing
// three values in each segment tree node
class Node {
public int pairs;
public int open;
public int closed;
public Node() {
pairs = open = closed = 0;
}
}
// function to get the middle
// index from corner indexes.
static int GetMid(int s, int e) {
return s + (e - s) / 2;
}
// Returns Parent Node after merging
// its left and right child
static Node Merge(Node left, Node right) {
Node parent = new Node();
int mini = Math.Min(left.open, right.closed);
parent.pairs = left.pairs + right.pairs + mini;
parent.open = left.open + right.open - mini;
parent.closed = left.closed + right.closed - mini;
return parent;
}
// A recursive function that constructs Segment Tree
// si is index of current node in segment tree head
static void ConstructSTUtil(string s, int ss,
int se, Node[] head, int si) {
// If there is one element in string, store it in
// current node of segment tree and return
if (ss == se) {
// since it contains one element, pairs
// will be zero
head[si].pairs = 0;
// check whether that one element is opening
// bracket or not
head[si].open = (s[ss] == '(' ? 1 : 0);
// check whether that one element is closing
// bracket or not
head[si].closed = (s[ss] == ')' ? 1 : 0);
return;
}
// If there are more than one elements, then recur
// for left and right subtrees and store the relation
// of values in this node
int mid = GetMid(ss, se);
ConstructSTUtil(s, ss, mid, head, si * 2 + 1);
ConstructSTUtil(s, mid + 1, se, head, si * 2 + 2);
// Merge left and right child into the Parent Node
head[si] = Merge(head[si * 2 + 1], head[si * 2 + 2]);
}
// Function to construct segment tree from given string
static Node[] BuildTree(string s) {
int n = s.Length;
// Height of segment tree
int x = (int)(Math.Ceiling(Math.Log(n, 2)));
// Maximum size of segment tree
int max_size = 2 * (int)Math.Pow(2, x) - 1;
// Declaring array of structure Allocate memory
Node[] head = new Node[max_size];
for (int i = 0; i < max_size; i++) {
head[i] = new Node();
}
// Fill the allocated memory head
ConstructSTUtil(s, 0, n - 1, head, 0);
// Return the constructed segment tree
return head;
}
// A Recursive function to get the desired
// Maximum Sum Sub-Array
static Node QueryUtil(Node[] head, int ss, int se,
int start, int end, int si) {
// No overlap
if (ss > end || se < start) {
// returns a Node for out of bounds condition
return new Node();
}
// Complete overlap
if (ss >= start && se <= end) {
return head[si];
}
// Partial Overlap Merge results of Left
// and Right subtrees
int mid = GetMid(ss, se);
Node left = QueryUtil(head, ss, mid, start, end, si * 2 + 1);
Node right = QueryUtil(head, mid + 1, se, start, end, si * 2 + 2);
// merge left and right subtree query results
return Merge(left, right);
}
// return length of maximum valid subsequence
static int Query(Node[] head, int start, int end, int n) {
Node res = QueryUtil(head, 0, n - 1, start, end, 0);
return 2 * res.pairs;
}
static List<int> SolveQueries(string s,
List<Tuple<int, int>> queries) {
int n = s.Length;
// to store the results of queries
List<int> result = new List<int>();
Node[] head = BuildTree(s);
// processing the queries
foreach (var q in queries) {
int start = q.Item1;
int end = q.Item2;
int res = Query(head, start, end, n);
result.Add(res);
}
return result;
}
public static void Main() {
string s = "())(())(())(";
List<Tuple<int, int>> queries = new List<Tuple<int, int>> {
Tuple.Create(4, 11),
Tuple.Create(3, 4),
Tuple.Create(0, 2),
Tuple.Create(0, 4),
Tuple.Create(1, 2)
};
List<int> ans = SolveQueries(s, queries);
foreach (int i in ans) {
Console.Write(i + " ");
}
}
}