Problem:
Problem A
Balanced Paths
You are given an undirected tree with $n$ nodes. The nodes are numbered 1 through $n$. Each node is labeled with either '(' or ')'. Let $l[u \rightarrow v]$ denote the string obtained by concatenating the labels of the nodes on the simple path from $u$ to $v$. (Note that the simple path between two nodes is uniquely determined on a tree.) A
balanced
string is defined as follows:
The empty string is balanced.
For any balanced string $s$, the string "(" $s$ ")" is balanced.
For any balanced strings $s$ and $t$, the string $st$ (the concatenation of $s$ and $t$) is balanced.
Any other string is NOT balanced.
Calculate the number of the ordered pairs of the nodes ($u$, $v$) such that $l[u \rightarrow v]$ is balanced.
Input
The input consists of a single test case. The input starts with an integer $n (2 \leq n \leq 10^5)$, which is the number of nodes of the tree. The next line contains a string of length $n$, each character of which is either '(' or ')'. The $x$-th character of the string represents the label of the node $x$ of the tree. Each of the following $n - 1$ lines contains two integers $a_i$ and $b_i$ $(1 \leq a_i, b_i \leq n)$, which represents that the node $a_i$ and the node $b_i$ are connected by an edge. The given graph is guaranteed to be a tree.
Output
Display a line containing the number of the ordered pairs ($u$, $v$) such that $l[u \rightarrow v]$ is balanced.
Sample Input 1
2
()
1 2
Output for the Sample Input 1
1
Sample Input 2
4
(())
1 2
2 3
3 4
Output for the Sample Input 2
2
Sample Input 3
5
()())
1 2
2 3
2 4
1 5
Output for the Sample Input 3
4

Submitted solutions:

code_snippet: #include <cstdio>
#define L 1<<23
#define C (c=*A++)
#define N 200001

int n,m,x,y,i,j,k,pr[N],sum,cnt,et=1,root,fe,hl,s[N],m1,m2,fw[N],e[N],f[N],g[N],cd[N<<1],nt[N<<1],wt,ss[19];char c,fl[L],*A=fl;bool u[N],v[N];long long MAPLE;

inline void read(int &x){x=0;while (C!=EOF&&'0'>c||c>'9');while ('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-48,C;}inline void print(long long x){if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}}
inline void core(int x,int f){bool tmp=true;s[x]=1;for (int y=e[x];y&&(!root||x==fe);y=nt[y]) if (!u[cd[y]]&&y!=f&&(core(cd[y],y^1),s[x]+=s[cd[y]],(s[cd[y]]<<1)>=sum)) tmp=false;if (tmp&&((s[x]<<1)>=sum)) {root=x,fe=cd[f];return;}}
inline void dfs(int x,int fe,int d1,int d2,int mx,int mi)
{
	if (d1-mx>=0) f[d1]++;if (d2-mi>=0) g[d2]++;
	for (int y=e[x];y;y=nt[y])
		if (!u[cd[y]]&&y!=fe)
			dfs(cd[y],y^1,d1+fw[cd[y]],d2-fw[cd[y]],mx>d1?mx:d1,mi>d2?mi:d2);
}
inline void vfs(int x,int fe,int d1,int d2,int mx,int mi)
{
	if (d1>m1) m1=d1;if (d1-mx>=0) MAPLE+=g[d1];
	if (d2>m2) m2=d2;if (d2-mi>=0) MAPLE+=f[d2];
	for (int y=e[x];y;y=nt[y])
		if (!u[cd[y]]&&y!=fe)
			vfs(cd[y],y^1,d1+fw[cd[y]],d2-fw[cd[y]],mx>d1?mx:d1,mi>d2?mi:d2);
}
inline void F(int x)
{
	int y;if (m1=m2=root=0,core(x,0),u[root]=true,s[fe]=sum-s[root],fw[root]==1) f[m1=1]=1;
	for (y=e[root];y;y=nt[y])
		if (!u[cd[y]])
		{
			vfs(cd[y],y^1,fw[root]+fw[cd[y]],-fw[cd[y]],fw[root]>0?fw[root]:0,0);
			dfs(cd[y],y^1,fw[root]+fw[cd[y]],-fw[cd[y]],fw[root]>0?fw[root]:0,0);
		}
	for (MAPLE+=f[0],i=m1;~i;f[i--]=0);for (i=m2;~i;g[i--]=0);
	for (y=e[root];y;y=nt[y]) if(!u[cd[y]]) sum=s[cd[y]],F(cd[y]);
}

int main()
{
	for (*(fl+fread(fl,1,L,stdin))=EOF,read(n);C!='('&&c!=')';);for (i=1;i<=n;fw[i]=c=='('?1:-1,C,i++);
	for (et=1,i=1;i<n;read(x),read(y),cd[++et]=y,nt[et]=e[x],e[x]=et,cd[++et]=x,nt[et]=e[y],e[y]=et,i++);
	MAPLE=0,sum=n,F(1),print(MAPLE);putchar('\n');
}
accuracy: 1
time_ms: 100
memory_kb: 18528
final_rank: 3

code_snippet: // AOJ #2687
// Balanced Paths 2025.2.14

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
 
const int NMAX = 100005;
 
int n;
string s;
vector<int> adj[NMAX];
bool removed[NMAX];
int subSize[NMAX];
 
int dfsSize(int u, int p) {
    subSize[u] = 1;
    for (int v : adj[u]) {
        if(v == p || removed[v]) continue;
        subSize[u] += dfsSize(v, u);
    }
    return subSize[u];
}
 
int findCentroid(int u, int p, int tot) {
    for (int v : adj[u]) {
        if(v == p || removed[v]) continue;
        if(subSize[v] > tot/2) return findCentroid(v, u, tot);
    }
    return u;
}
 
void dfsBranch(int u, int p, int cur, int curMin, int curMax, vector<array<int,3>> &rec) {
    rec.push_back({cur, curMin, curMax});
    for (int v : adj[u]) {
        if(v == p || removed[v]) continue;
        int add = (s[v-1]=='(' ? 1 : -1);
        int ncur = cur + add;
        int nmin = min(curMin, ncur);
        int nmax = max(curMax, ncur);
        dfsBranch(v, u, ncur, nmin, nmax, rec);
    }
}
 
int countCandidates(const vector<pair<int,int>> &vec, int target, int threshold) {
    auto lb = std::lower_bound(vec.begin(), vec.end(), make_pair(target, threshold),
        [](const pair<int,int>& a, const pair<int,int>& b){
            if(a.first == b.first) return a.second < b.second;
            return a.first < b.first;
        });
    auto ub = std::upper_bound(vec.begin(), vec.end(), make_pair(target, INT_MAX),
        [](const pair<int,int>& a, const pair<int,int>& b){
            if(a.first == b.first) return a.second < b.second;
            return a.first < b.first;
        });
    return distance(lb, ub);
}
 
ll ans = 0;
 
void decompose(int start) {
    int tot = dfsSize(start, -1);
    int cen = findCentroid(start, -1, tot);
    removed[cen] = true;
 
    int cVal = (s[cen-1]=='(' ? 1 : -1);
 
    vector< vector< pair<int,int> > > branchForward;
    vector< vector<int> > branchReversed;
    vector< pair<int,int> > globalForward;
 
    for (int v : adj[cen]) {
        if(removed[v]) continue;
        vector<array<int,3>> rec; 
        int add = (s[v-1]=='(' ? 1 : -1);
        int init = add;
        int initMin = min(0, init);
        int initMax = max(0, init);
        dfsBranch(v, cen, init, initMin, initMax, rec);
 
        vector< pair<int,int> > fwd;
        vector<int> rev;
        for (auto &arr : rec) {
            int bal = arr[0], mn = arr[1], mx = arr[2];
            fwd.push_back({bal, mn});
            if(mx <= bal)
                rev.push_back(bal);
        }
        sort(fwd.begin(), fwd.end(), [](const pair<int,int>& a, const pair<int,int>& b) {
            if(a.first == b.first) return a.second < b.second;
            return a.first < b.first;
        });
        sort(rev.begin(), rev.end());
 
        branchForward.push_back(fwd);
        branchReversed.push_back(rev);
        for (auto &p : fwd) globalForward.push_back(p);
    }
    sort(globalForward.begin(), globalForward.end(), [](const pair<int,int>& a, const pair<int,int>& b) {
        if(a.first == b.first) return a.second < b.second;
        return a.first < b.first;
    });
 
    for (auto &vec : branchForward) {
        for (auto &p : vec) {
            int bal = p.first, mn = p.second;
            if(cVal + bal == 0 && cVal + mn >= 0) ans++;
        }
    }
    for (auto &vec : branchReversed) {
        for (int a : vec) {
            if(a + cVal == 0) ans++;
        }
    }
 
    int brCount = branchForward.size();
    for (int i = 0; i < brCount; i++) {
        for (int a : branchReversed[i]) {
            if(a + cVal < 0) continue;
            int target = - (a + cVal);
            int thr = - (a + cVal);
            int totCount = countCandidates(globalForward, target, thr);
            int localCount = countCandidates(branchForward[i], target, thr);
            ans += (totCount - localCount);
        }
    }
 
    for (int v : adj[cen]) {
        if(!removed[v]) decompose(v);
    }
}
 
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
 
    cin >> n;
    cin >> s;
    for (int i = 1; i <= n; i++){
        adj[i].clear();
        removed[i] = false;
    }
    for (int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    ans = 0;
    decompose(1);
    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 20376
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
#include <atcoder/all>
#endif
using ll=long long;
using ull=unsigned long long;
using P=pair<ll,ll>;
template<typename T>using minque=priority_queue<T,vector<T>,greater<T>>;
template<typename T>bool chmax(T &a,const T &b){return (a<b?(a=b,true):false);}
template<typename T>bool chmin(T &a,const T &b){return (a>b?(a=b,true):false);}
template<typename T1,typename T2>istream &operator>>(istream &is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T>istream &operator>>(istream &is,vector<T> &a){for(auto &i:a)is>>i;return is;}
template<typename T1,typename T2>void operator++(pair<T1,T2>&a,int n){a.first++,a.second++;}
template<typename T1,typename T2>void operator--(pair<T1,T2>&a,int n){a.first--,a.second--;}
template<typename T>void operator++(vector<T>&a,int n){for(auto &i:a)i++;}
template<typename T>void operator--(vector<T>&a,int n){for(auto &i:a)i--;}
#define reps(i,a,n) for(int i=(a);i<int(n);i++)
#define rep(i,n) reps(i,0,n)
#define all(x) x.begin(),x.end()
#define pcnt(x) __builtin_popcountll(x)
#define fin(x) return cout<<x<<'\n',static_cast<void>(0)
ll myceil(ll a,ll b){return (a+b-1)/b;}
template<typename T,size_t n,size_t id=0>
auto vec(const int (&d)[n],const T &init=T()){
  if constexpr (id<n)return vector(d[id],vec<T,n,id+1>(d,init));
  else return init;
}
#ifdef LOCAL
#include<debug.h>
#else
#define debug(...) static_cast<void>(0)
#define debugg(...) static_cast<void>(0)
template<typename T1,typename T2>ostream &operator<<(ostream &os,const pair<T1,T2>&p){os<<p.first<<' '<<p.second;return os;}
#endif
void SOLVE();
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout<<fixed<<setprecision(16);
  #ifdef LOCAL
  clock_t start=clock();
  #endif
  int testcase=1;
  //cin>>testcase;
  for(int i=0;i<testcase;i++){
    SOLVE();
  }
  #ifdef LOCAL
  cerr<<"time:";
  cerr<<(clock()-start)/1000;
  cerr<<"ms\n";
  #endif
}
template<typename T=int>
struct Edge{
  int from,to;
  T weight;
  int index;
  Edge(int from_,int to_,T weight_=T(),int index_=-1):from(from_),to(to_),weight(weight_),index(index_){}
  Edge():from(-1),to(-1),weight(),index(-1){}
  friend std::ostream &operator<<(std::ostream &os,const Edge&e){
    os<<'[';
    os<<"from:"<<e.from;
    os<<"to:"<<e.to;
    os<<"weight:"<<e.weight;
    os<<"index:"<<e.index;
    os<<']';
    return os;
  }
};
using namespace std;
template<typename T=int>
struct Tree{
private:
  int n;
  vector<int>par;
  vector<Edge<T>>edge;
  vector<Edge<T>>g;
  vector<int>ptr;
  struct tree_range{
    using iterator=typename vector<Edge<T>>::iterator;
    iterator l,r;
    iterator begin()const{return l;}
    iterator end()const{return r;}
    int size()const{return r-l;}
    bool empty()const{return !size();}
    Edge<T> &operator[](int i)const{return l[i];}
  };
  struct const_tree_range{
    using iterator=typename vector<Edge<T>>::const_iterator;
    iterator l,r;
    iterator begin()const{return l;}
    iterator end()const{return r;}
    int size()const{return r-l;}
    bool empty()const{return !size();}
    const Edge<T> &operator[](int i)const{return l[i];}
  };
public:
  Tree(int n_):n(n_){
    edge.reserve(n-1);
  }
  Tree():n(0){}
  Tree(int n_,const vector<Edge<T>>&e):n(n_),edge(e){
    build();
  }
  template<bool weighted=false,bool index=1>
  void read(){
    for(int i=0;i<n-1;i++){
      int u,v;
      T w=T();
      cin>>u>>v;
      if constexpr(index)u--,v--;
      if constexpr(weighted)cin>>w;
      else w=1;
      edge.emplace_back(u,v,w,i);
    }
    build();
  }
  template<bool index=1>
  void readp(){
    par.resize(n);
    par[0]=-1;
    ptr.resize(n+1);
    for(int i=1;i<n;i++){
      int p;
      cin>>p;
      if constexpr(index)p--;
      edge.emplace_back(p,i,1,i-1);
      par[i]=p;
      ptr[p]++;
    }
    for(int i=1;i<=n;i++)ptr[i]+=ptr[i-1];
    g.resize(n-1);
    for(int i=0;i<n-1;i++){
      g[--ptr[edge[i].from]]=edge[i];
    }
  }
  void add_edge(int u,int v){edge.emplace_back(u,v,1,edge.size());}
  void add_edge(int u,int v,T w){edge.emplace_back(u,v,w,edge.size());}
  void add_edge(int u,int v,T w,int idx){edge.emplace_back(u,v,w,idx);}
  inline bool is_directed()const{return !par.empty();}
  void build(){
    ptr.resize(n+1,0);
    for(auto&&[u,v,w,i]:edge)ptr[u]++,ptr[v]++;
    for(int i=1;i<=n;i++)ptr[i]+=ptr[i-1];
    assert(ptr[n]==n*2-2);
    g.resize(n*2-2);
    par.clear();
    for(auto&&[u,v,w,i]:edge){
      g[--ptr[u]]=Edge(u,v,w,i);
      g[--ptr[v]]=Edge(v,u,w,i);
    }
  }
  void remove_parent(int root=0){
    edge.resize(n-1);
    par.resize(n);
    auto dfs=[&](auto self,int x,int p,T w)->void {
      par[x]=p;
      for(const Edge<T>&e:(*this)[x])if(e.to!=p){
        edge[e.index]=e;
        self(self,e.to,x,e.weight);
      }
    };
    dfs(dfs,root,-1,T());
    ptr.resize(n+1);
    std::fill(ptr.begin(),ptr.end(),0);
    for(int i=0;i<n-1;i++)ptr[edge[i].from]++;
    for(int i=1;i<=n;i++)ptr[i]+=ptr[i-1];
    g.resize(ptr[n]);
    for(const Edge<T>&e:edge)g[--ptr[e.from]]=e;
  }
  vector<int>bfs_order(){
    assert(is_directed());
    vector<int>bfs(n);
    int p=0,q=0;
    bfs[q++]=root();
    while(p<q){
      int x=bfs[p++];
      for(const Edge<T>&e:(*this)[x])bfs[q++]=e.to;
    }
    return bfs;
  }
  std::vector<int>dfs_order(){
    assert(is_directed());
    std::vector<int>res;
    res.reserve(n);
    std::vector<int>st(n);
    int p=0;
    st[p++]=root();
    while(p){
      int x=st[--p];
      res.push_back(x);
      for(const Edge<T>&e:(*this)[x])st[p++]=e.to;
    }
    return res;
  }
  void hld(){
    assert(is_directed());
    auto dfs=[&](auto self,int x)->int {
      int ret=1,mx=-1;
      for(Edge<T>&e:(*this)[x]){
        int s=self(self,e.to);
        ret+=s;
        if(mx<s){
          mx=s;
          swap((*this)[x][0],e);
        }
      }
      return ret;
    };
    dfs(dfs,root());
  }
  std::pair<std::vector<int>,std::vector<int>>in_out_order(){
    assert(is_directed());
    std::vector<int>in(n),out(n);
    int p=0;
    auto dfs=[&](auto self,int x)->void {
      in[x]=p++;
      for(const Edge<T>&e:(*this)[x]){
        self(self,e.to);
      }
      out[x]=p;
    };
    dfs(dfs,root());
    return std::make_pair(in,out);
  }
  int size()const{return n;}
  tree_range operator[](int i){return tree_range{g.begin()+ptr[i],g.begin()+ptr[i+1]};}
  const_tree_range operator[](int i)const{return const_tree_range{g.begin()+ptr[i],g.begin()+ptr[i+1]};}
  const Edge<T>& get_edge(int i)const{return edge[i];}
  inline int parent(int i)const{return par[i];}
  inline int root()const{return std::find(par.begin(),par.end(),-1)-par.begin();}
};
using namespace std;
template<typename T=int>
struct CompressedTree:Tree<T>{
private:
  vector<int>mapping;
public:
  CompressedTree(const vector<int>&mapping_,const Tree<T>&t_):mapping(mapping_),Tree<T>(t_){}
  CompressedTree(const Tree<T>&t_):mapping(t_.size()),Tree<T>(t_){
    iota(mapping.begin(),mapping.end(),0);
  }
  CompressedTree():Tree<T>(){}
  int get(int u)const{return mapping[u];}
};
template<typename T,typename Func>
void centroid_decomposition(const Tree<T>&t,const Func&f){
  vector<int>sub(t.size());
  auto centroid=[&](auto self,const CompressedTree<T>&g,int x,int p)->int {
    sub[x]=1;
    for(const auto&e:g[x])if(e.to!=p){
      int c=self(self,g,e.to,x);
      if(c!=-1)return c;
      sub[x]+=sub[e.to];
    }
    if(g.size()-sub[x]<=g.size()/2){
      if(p!=-1)sub[p]=g.size()-sub[x];
      return x;
    }
    return -1;
  };
  auto rec=[&](auto self,const CompressedTree<T>&g)->void {
    if(g.size()<=2)return;
    int c=centroid(centroid,g,0,-1);
    vector<int>is_left(g[c].size());
    int lsize=1,rsize=1;
    int sum=0;
    int idx=0;
    for(const auto&e:g[c]){
      if(sum+sub[e.to]<=(g.size()-1)*2/3){
        is_left[idx]=1;
        lsize+=sub[e.to];
      }
      else{
        is_left[idx]=0;
        rsize+=sub[e.to];
      }
      sum+=sub[e.to];
      idx++;
    }
    vector<int>mapping_left{g.get(c)},mapping_right{g.get(c)};
    mapping_left.reserve(lsize),mapping_right.reserve(rsize);
    Tree<T>left(lsize),right(rsize);
    auto dfsl=[&](auto self,int x,int p,int pid,T v)->void {
      int nid=mapping_left.size();
      left.add_edge(nid,pid,v);
      mapping_left.push_back(g.get(x));
      for(const auto&e:g[x])if(e.to!=p)self(self,e.to,x,nid,e.weight);
    };
    auto dfsr=[&](auto self,int x,int p,int pid,T v)->void {
      int nid=mapping_right.size();
      right.add_edge(nid,pid,v);
      mapping_right.push_back(g.get(x));
      for(const auto&e:g[x])if(e.to!=p)self(self,e.to,x,nid,e.weight);
    };
    idx=0;
    for(const auto&e:g[c]){
      if(is_left[idx++])dfsl(dfsl,e.to,c,0,e.weight);
      else dfsr(dfsr,e.to,c,0,e.weight);
    }
    left.build(),right.build();
    CompressedTree<T>cleft(mapping_left,left),cright(mapping_right,right);
    self(self,cleft);
    self(self,cright);
    f(cleft,cright);
  };
  rec(rec,t);
}
void SOLVE(){
  int n;
  string s;
  cin>>n>>s;
  Tree t(n);
  t.read();
  ll ans=0;
  auto f=[&](auto lhs,auto rhs)->void {
    map<int,int>mp;
    auto ldfs=[&](auto self,int x,int p,int dep,int mx)->void {
      if(x!=0){
        if(dep==mx)mp[dep]++;
      }
      for(auto i:lhs[x])if(i.to!=p){
        int d=dep+(s[lhs.get(i.to)]=='('?1:-1);
        self(self,i.to,x,d,max(mx,d));
      }
    };
    auto rdfs=[&](auto self,int x,int p,int dep,int mx)->void {
      if(x!=0){
        if(dep==mx)ans+=mp[dep];
      }
      for(auto i:rhs[x])if(i.to!=p){
        int d=dep+(s[rhs.get(i.to)]=='('?-1:1);
        self(self,i.to,x,d,max(mx,d));
      }
    };
    ldfs(ldfs,0,-1,0,0);
    int d=s[rhs.get(0)]=='('?-1:1;
    rdfs(rdfs,0,-1,d,max(d,0));
    mp.clear();
    swap(lhs,rhs);
    ldfs(ldfs,0,-1,0,0);
    rdfs(rdfs,0,-1,d,max(d,0));
  };
  centroid_decomposition(t,f);
  rep(i,n-1)ans+=s[t.get_edge(i).from]!=s[t.get_edge(i).to];
  cout<<ans<<endl;
}
accuracy: 1
time_ms: 220
memory_kb: 48432
final_rank: 11

code_snippet: #include <bits/stdc++.h>




namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


namespace zawa {

void SetFastIO() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
}

void SetPrecision(u32 dig) {
    std::cout << std::fixed << std::setprecision(dig);
}

} // namespace zawa
using namespace zawa;

using Item = std::pair<int, int>;
std::ostream& operator<<(std::ostream& os, Item& i) {
    os << '(' << i.first << ',' << i.second << ')';
    return os;
}
Item identity() {
    return Item{0,0};
}
Item read(char c) {
    assert(c == '(' or c == ')');
    return (c == '(' ? Item{1,1} : Item{-1,-1});
}
Item operation(Item L, Item R) {
    return Item{L.first + R.first, std::min(L.second, L.first + R.second)};
}
std::pair<bool, Item> balancePair(Item v) {
    if (v.second < 0) return { false, identity() };
    return std::make_pair( true, Item{ -v.first, -v.first } );
}

int main() {
    SetFastIO();

    int N;
    std::cin >> N;
    std::string S;
    std::cin >> S;
    std::vector<std::vector<int>> g(N);
    for (int _{} ; _ < N - 1 ; _++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }
    std::vector<bool> removed(N);
    std::vector<int> sz(N);
    auto calc_sz{[&](auto dfs, int v, int p) -> int {
        sz[v] = 1;
        for (auto x : g[v]) if (!removed[x] and x != p) {
            sz[v] += dfs(dfs, x, v);
        }
        return sz[v];
    }};
    // val: v -> cent (centを含む)
    // inv_val: cent -> v (centを含まない)
    std::vector<Item> val(N), inv_val(N);
    auto calc_vals{[&](auto dfs, int v, int p, Item cur, Item inv_cur) -> void {
        val[v] = cur = operation(read(S[v]), cur);
        inv_val[v] = inv_cur = operation(inv_cur, read(S[v]));
        for (auto x : g[v]) if (!removed[x] and x != p) {
            dfs(dfs, x, v, cur, inv_cur);
        }
    }};
    auto calc_vs{[&](auto dfs, int v, int p, std::vector<int>& cur) -> void {
        cur.push_back(v);
        for (auto x : g[v]) if (!removed[x] and x != p) {
            dfs(dfs, x, v, cur);
        }
    }};
    long long ans{};
    auto cent{[&](auto dfs, int v) -> void {
        calc_sz(calc_sz, v, -1);
        int p{-1};
        int all{sz[v]};
        bool fn{};
        while (!fn) {
            fn = true;
            for (auto x : g[v]) if (x != p and !removed[x] and 2 * sz[x] > all) {
                fn = false;
                p = v;
                v = x;
                break;
            } 
        }
        std::vector<std::vector<int>> subtree;
        for (auto x : g[v]) if (!removed[x]) {
            calc_vals(calc_vals, x, v, read(S[v]), identity());
            std::vector<int> vs;
            calc_vs(calc_vs, x, v, vs);
            subtree.push_back(vs);
        }
        // std::cout << "cent is " << v + 1 << std::endl;
        // for (auto x : val) std::cout << x << ' ';
        // std::cout << std::endl;
        // for (auto x : inv_val) std::cout << x << ' ';
        // std::cout << std::endl;
        std::map<Item, int> map;
        for (const auto& arr : subtree) {
            for (auto x : arr) {
                if (val[x] == Item{ 0, 0 }) ans++;
                map[inv_val[x]]++;
            }
        }
        if (S[v] == '(') {
            auto it{map.find(Item{ -1, -1 })};
            if (it != map.end()) ans += it->second;
        }
        for (const auto& arr : subtree) {
            for (auto x : arr) map[inv_val[x]]--;
            for (auto x : arr) {
                auto [flag, bal]{balancePair(val[x])};
                if (flag) {
                    auto it{map.find(bal)};
                    if (it != map.end()) ans += it->second;
                }
            }
            for (auto x : arr) map[inv_val[x]]++;
        }
        // std::cout << ans << std::endl;
        // exit(0);
        removed[v] = true;
        for (auto x : g[v]) if (!removed[x]) {
            dfs(dfs, x);
        }
    }};
    cent(cent, 0);
    std::cout << ans << '\n';
}
accuracy: 1
time_ms: 310
memory_kb: 22344
final_rank: 13

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

// level、i番目の重心の連結成分数、一個前のlevelの頂点
tuple<vector<int>, vector<int>, vector<int>> centroid_decomposition(const vector<vector<int>> &g) {
    int N = g.size();
    vector<int> sz(N), level(N, -1), sz_comp(N), par(N);

    auto dfs_size = [&](auto self, int s, int p) -> int {
        sz[s] = 1;
        for (int t : g[s]) {
            if (t != p and level[t] == -1) sz[s] += self(self, t, s);
        }
        return sz[s];
    };

    auto dfs_centroid = [&](auto self, int s, int p, int n) -> int {
        for (int t : g[s]) {
            if (t != p and level[t] == -1 and sz[t] > n/2) {
                return self(self, t, s, n);
            }
        }
        return s;
    };

    auto decompose = [&](auto self, int s, int p, int lev) -> void {
        int n = dfs_size(dfs_size, s, p);
        int c = dfs_centroid(dfs_centroid, s, p, n);
        level[c] = lev;
        sz_comp[c] = n;
        par[c] = p;
        for (int t : g[c]) {
            if (level[t] == -1) self(self, t, c, lev+1);
        }
    };

    decompose(decompose, 0, -1, 0);
    return {level, sz_comp, par};
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n; cin >> n;
    string str; cin >> str;
    vector<vector<int>> g(n);
    for (int i = 0; i < n-1; ++i) {
        int x,y; cin >> x >> y;
        x -= 1; y -= 1;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    auto [level, sz_comp, par] = centroid_decomposition(g);
    ll res = 0;
    for (int i = 0; i < n; ++i) {
        int m = sz_comp[i];
        vector<int> pre(m+1), bac(m+1);
        vector<int> m_pre(m+1), m_bac(m+1);
        int lev = level[i];

        auto dfs1 = [&](auto dfs1, int s, int p, int lev, int cur, int need) -> int {
            int sz = 1;
            if (str[s] == '(') {
                cur += 1;
                if (need) need -= 1;
                if (need == 0 and cur >= 0) m_pre[cur] += 1;
            }
            else {
                cur -= 1;
                need += 1;
            }
            for (int t : g[s]) {
                if (level[t] >= lev and t != p) {
                    sz += dfs1(dfs1, t, s, lev, cur, need);
                }
            }
            return sz;
        };
        auto dfs2 = [&](auto dfs2, int s, int p, int lev, int cur, int need) -> int {
            int sz = 1;
            if (str[s] == ')') {
                cur += 1;
                if (need) need -= 1;
                if (need == 0 and cur >= 0) m_bac[cur] += 1;
            }
            else {
                cur -= 1;
                need += 1;
            }
            for (int t : g[s]) {
                if (level[t] >= lev and t != p) {
                    sz += dfs2(dfs2, t, s, lev, cur, need);
                }
            }
            return sz;
        };

        for (int t : g[i]) {
            if (level[t] >= level[i]) {
                int nn = dfs1(dfs1, t, i, lev, 0, 0);
                dfs2(dfs2, t, i, lev, 0, 0);

                // 折り返さないパス
                if (str[i] == ')') res += m_pre[1];
                else res += m_bac[1];

                // 折り返すパス
                for (int j = 0; j <= nn; ++j) {
                    if (str[i] == '(') {
                        if (j+1 <= m) res += (ll)m_pre[j] * bac[j+1];
                        if (j) res += (ll)pre[j-1] * m_bac[j];
                    }
                    else {
                        if (j) res += (ll)m_pre[j] * bac[j-1];
                        if (j+1 <= m) res += (ll)pre[j+1] * m_bac[j];
                    }
                }
                // マージ
                for (int j = 0; j <= nn; ++j) {
                    pre[j] += m_pre[j]; m_pre[j] = 0;
                    bac[j] += m_bac[j]; m_bac[j] = 0;
                }
            }
        }
    }
    cout << res << endl;
}
accuracy: 1
time_ms: 170
memory_kb: 22244
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;

long long solve(int N, const string& S, const vector<int>& A, const vector<int>& B) {
	// step #1. construct graph
	vector<vector<int> > G(N);
	for (int i = 0; i < N - 1; i++) {
		G[A[i]].push_back(B[i]);
		G[B[i]].push_back(A[i]);
	}

	// step #2. find centroid
	vector<int> subsize(N), par(N);
	auto build_tree = [&](auto& self, int pos, int pre) -> void {
		par[pos] = pre;
		subsize[pos] = 1;
		for (int i : G[pos]) {
			if (i != pre) {
				self(self, i, pos);
				subsize[pos] += subsize[i];
			}
		}
	};
	build_tree(build_tree, 0, -1);
	int root = -1;
	for (int i = 0; i < N; i++) {
		bool flag = (subsize[i] * 2 >= N);
		for (int j : G[i]) {
			if (j != par[i] && subsize[j] * 2 >= N) {
				flag = false;
			}
		}
		if (flag) {
			root = i;
			break;
		}
	}

	// step #3. calculation of depths
	vector<int> group(N, -1);
	vector<vector<int> > dp(N, vector<int>(3));
	auto dfs = [&](auto& self, int pos, int pre) -> void {
		for (int i : G[pos]) {
			if (i != pre) {
				if (pos != root) {
					group[i] = group[pos];
				}
				dp[i][0] = dp[pos][0] + (S[i] == '(' ? +1 : -1);
				dp[i][1] = max(dp[pos][1], dp[i][0]);
				dp[i][2] = min(dp[pos][2], dp[i][0]);
				self(self, i, pos);
			}
		}
	};
	int K = G[root].size();
	for (int i = 0; i < K; i++) {
		group[G[root][i]] = i;
	}
	dfs(dfs, root, -1);
	vector<int> vl(N, -1), vr(N, -1);
	for (int i = 0; i < N; i++) {
		if (dp[i][0] == dp[i][1]) {
			vl[i] = dp[i][0];
		}
		if (dp[i][0] == min(dp[i][2], int(S[root] == '(' ? -1 : +1))) {
			vr[i] = -(dp[i][0] + int(S[root] == '(' ? +1 : -1));
		}
	}

	// step #4. calculate answer for paths passing root
	vector<vector<int> > comps(K);
	vector<int> comp_id(N, -1);
	for (int i = 0; i < N; i++) {
		if (i != root) {
			comp_id[i] = comps[group[i]].size();
			comps[group[i]].push_back(i);
		}
	}
	long long ans1 = 0;
	vector<int> cl(N + 1, 0), cr(N + 1, 0);
	for (int i = 0; i < N; i++) {
		if (vl[i] != -1) cl[vl[i]] += 1;
		if (vr[i] != -1) cr[vr[i]] += 1;
	}
	for (int i = 0; i < N; i++) {
		ans1 += 1LL * cl[i] * cr[i];
	}
	for (int i = 0; i < K; i++) {
		vector<int> subcl(comps[i].size() + 2), subcr(comps[i].size() + 2);
		for (int j : comps[i]) {
			if (vl[j] != -1) subcl[vl[j]] += 1;
			if (vr[j] != -1) subcr[vr[j]] += 1;
		}
		for (int j = 0; j <= comps[i].size() + 1; j++) {
			ans1 -= 1LL * subcl[j] * subcr[j];
		}
	}

	// step #5. decompose to smaller trees
	vector<vector<int> > suba(K), subb(K);
	for (int i = 0; i < N - 1; i++) {
		if (A[i] != root && B[i] != root) {
			suba[group[A[i]]].push_back(comp_id[A[i]]);
			subb[group[B[i]]].push_back(comp_id[B[i]]);
		}
	}
	long long ans2 = 0;
	for (int i = 0; i < K; i++) {
		int subn = comps[i].size();
		string subs;
		for (int j : comps[i]) {
			subs += S[j];
		}
		long long res = solve(subn, subs, suba[i], subb[i]);
		ans2 += res;
	}

	return ans1 + ans2;
}

int main() {
	int N; string S;
	cin >> N >> S;
	vector<int> A(N - 1), B(N - 1);
	for (int i = 0; i < N - 1; i++) {
		cin >> A[i] >> B[i];
		A[i] -= 1;
		B[i] -= 1;
	}
	long long ans = solve(N, S, A, B);
	cout << ans << endl;
	return 0;
}
accuracy: 1
time_ms: 320
memory_kb: 42676
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;

string to_string(const vector<int>& arr) {
	string res = "[";
	for (int i = 0; i < arr.size(); i++) {
		if (i != 0) {
			res += ", ";
		}
		res += to_string(arr[i]);
	}
	res += "]";
	return res;
}

long long solve(int N, const string& S, const vector<int>& A, const vector<int>& B) {
	// step #1. construct graph
	vector<vector<int> > G(N);
	for (int i = 0; i < N - 1; i++) {
		G[A[i]].push_back(B[i]);
		G[B[i]].push_back(A[i]);
	}

	// step #2. find centroid
	vector<int> subsize(N), par(N);
	auto build_tree = [&](auto& self, int pos, int pre) -> void {
		par[pos] = pre;
		subsize[pos] = 1;
		for (int i : G[pos]) {
			if (i != pre) {
				self(self, i, pos);
				subsize[pos] += subsize[i];
			}
		}
	};
	build_tree(build_tree, 0, -1);
	int root = -1;
	for (int i = 0; i < N; i++) {
		bool flag = (subsize[i] * 2 >= N);
		for (int j : G[i]) {
			if (j != par[i] && subsize[j] * 2 >= N) {
				flag = false;
			}
		}
		if (flag) {
			root = i;
			break;
		}
	}

	// step #3. calculation of depths
	vector<int> group(N, -1);
	vector<vector<int> > dp(N, vector<int>(3));
	auto dfs = [&](auto& self, int pos, int pre) -> void {
		for (int i : G[pos]) {
			if (i != pre) {
				if (pos != root) {
					group[i] = group[pos];
				}
				dp[i][0] = dp[pos][0] + (S[i] == '(' ? +1 : -1);
				dp[i][1] = max(dp[pos][1], dp[i][0]);
				dp[i][2] = min(dp[pos][2], dp[i][0]);
				self(self, i, pos);
			}
		}
	};
	int K = G[root].size();
	for (int i = 0; i < K; i++) {
		group[G[root][i]] = i;
	}
	dfs(dfs, root, -1);
	vector<int> vl(N, -1), vr(N, -1);
	for (int i = 0; i < N; i++) {
		if (dp[i][0] == dp[i][1]) {
			vl[i] = dp[i][0];
		}
		if (dp[i][0] == min(dp[i][2], int(S[root] == '(' ? -1 : +1))) {
			vr[i] = -(dp[i][0] + int(S[root] == '(' ? +1 : -1));
		}
	}

	// step #4. calculate answer for paths passing root
	vector<vector<int> > comps(K);
	vector<int> comp_id(N, -1);
	for (int i = 0; i < N; i++) {
		if (i != root) {
			comp_id[i] = comps[group[i]].size();
			comps[group[i]].push_back(i);
		}
	}
	long long ans1 = 0;
	vector<int> cl(N + 1, 0), cr(N + 1, 0);
	for (int i = 0; i < N; i++) {
		if (vl[i] != -1) cl[vl[i]] += 1;
		if (vr[i] != -1) cr[vr[i]] += 1;
	}
	for (int i = 0; i < N; i++) {
		ans1 += 1LL * cl[i] * cr[i];
	}
	for (int i = 0; i < K; i++) {
		vector<int> subcl(comps[i].size() + 2), subcr(comps[i].size() + 2);
		for (int j : comps[i]) {
			if (vl[j] != -1) subcl[vl[j]] += 1;
			if (vr[j] != -1) subcr[vr[j]] += 1;
		}
		for (int j = 0; j <= comps[i].size() + 1; j++) {
			ans1 -= 1LL * subcl[j] * subcr[j];
		}
	}

	// step #5. decompose to smaller trees
	vector<vector<int> > suba(K), subb(K);
	for (int i = 0; i < N - 1; i++) {
		if (A[i] != root && B[i] != root) {
			suba[group[A[i]]].push_back(comp_id[A[i]]);
			subb[group[B[i]]].push_back(comp_id[B[i]]);
		}
	}
	long long ans2 = 0;
	for (int i = 0; i < K; i++) {
		int subn = comps[i].size();
		string subs;
		for (int j : comps[i]) {
			subs += S[j];
		}
		long long res = solve(subn, subs, suba[i], subb[i]);
		ans2 += res;
	}

	return ans1 + ans2;
}

int main() {
	int N; string S;
	cin >> N >> S;
	vector<int> A(N - 1), B(N - 1);
	for (int i = 0; i < N - 1; i++) {
		cin >> A[i] >> B[i];
		A[i] -= 1;
		B[i] -= 1;
	}
	long long ans = solve(N, S, A, B);
	cout << ans << endl;
	return 0;
}
accuracy: 1
time_ms: 320
memory_kb: 42592
final_rank: 14

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n; cin >> n;
    string str; cin >> str;
    vector<vector<int>> g(n);
    for (int i = 0; i < n - 1; ++i) {
        int x,y; cin >> x >> y;
        x--; y--;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    ll res = 0;
    vector<deque<int>> pdp(n), sdp(n);
    auto dfs = [&](auto dfs,int s,int p)->void{
        vector<int> v;
        for(int t:g[s]){
            if(t == p) continue;
            dfs(dfs, t, s);
            v.push_back(t);
        }
        if(v.size() == 0){
            if(str[s] == '('){
                pdp[s].push_back(0);
                pdp[s].push_back(1);
                sdp[s].push_back(0);
                sdp[s].push_back(0);
            }
            else{
                pdp[s].push_back(0);
                pdp[s].push_back(0);
                sdp[s].push_back(0);
                sdp[s].push_back(1);
            }
        }
        else{
            sort(v.begin(), v.end(), [&](auto i,auto j){
               return pdp[i].size() > pdp[j].size();
            });
            // マージ
            // 枝分かれするパスの数え上げもここで行う
            swap(pdp[s], pdp[v[0]]);
            swap(sdp[s], sdp[v[0]]);
            while(v.size() > 1){
                int t = v.back(); v.pop_back();
                if(str[s] == '('){
                    for (int i = 0; i < pdp[t].size(); ++i) {
                        if(i+1 < sdp[s].size()){
                            res += (ll)pdp[t][i] * (ll)sdp[s][i+1];
                        }
                        if(i){
                            res += (ll)pdp[s][i-1] * (ll)sdp[t][i];
                        }
                    }
                }
                else{
                    for (int i = 0; i < pdp[t].size(); ++i) {
                        if(i){
                            res += (ll)pdp[t][i] * (ll)sdp[s][i-1];
                        }
                        if(i+1 < pdp[s].size()){
                            res += (ll)pdp[s][i+1] * (ll)sdp[t][i];
                        }
                    }
                }
                for (int i = 0; i < pdp[t].size(); ++i) {
                    pdp[s][i] += pdp[t][i];
                    sdp[s][i] += sdp[t][i];
                }
            }
            if(str[s] == '('){
                pdp[s].push_front(0);
                sdp[s].pop_front();
                sdp[s].push_back(0);
                sdp[s].push_back(0);
                pdp[s][1]++;
            }
            else{
                sdp[s].push_front(0);
                pdp[s].pop_front();
                pdp[s].push_back(0);
                pdp[s].push_back(0);
                sdp[s][1]++;
            }
            res += pdp[s][0] + sdp[s][0]; // 枝分かれしないパス
        }
//        cout << s+1 << endl;
//        for (int i = 0; i < pdp[s].size(); ++i) {
//            cout << pdp[s][i] << " ";
//        }
//        cout << endl;
//        for (int i = 0; i < sdp[s].size(); ++i) {
//            cout << sdp[s][i] << " ";
//        }
//        cout << endl;
    };
    dfs(dfs, 0, -1);
    cout << res << endl;
}
accuracy: 1
time_ms: 120
memory_kb: 170480
final_rank: 16

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n; cin >> n;
    string str; cin >> str;
    vector<vector<int>> g(n);
    for (int i = 0; i < n - 1; ++i) {
        int x,y; cin >> x >> y;
        x--; y--;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    vector<int> sz(n);
    ll res = 0;
    vector<deque<int>> pdp(n), sdp(n);
    auto dfs = [&](auto dfs,int s,int p)->void{
        sz[s] = 1;
        vector<int> v;
        for(int t:g[s]){
            if(t == p) continue;
            dfs(dfs, t, s);
            sz[s] += sz[t];
            v.push_back(t);
        }
        if(v.size() == 0){
            if(str[s] == '('){
                pdp[s].push_back(0);
                pdp[s].push_back(1);
                sdp[s].push_back(0);
                sdp[s].push_back(0);
            }
            else{
                pdp[s].push_back(0);
                pdp[s].push_back(0);
                sdp[s].push_back(0);
                sdp[s].push_back(1);
            }
        }
        else{
            sort(v.begin(), v.end(), [&](auto i,auto j){
               return sz[i] > sz[j];
            });
            // マージ
            // 枝分かれするパスの数え上げもここで行う
            swap(pdp[s], pdp[v[0]]);
            swap(sdp[s], sdp[v[0]]);
            while(v.size() > 1){
                int t = v.back(); v.pop_back();
                if(str[s] == '('){
                    for (int i = 0; i < pdp[t].size(); ++i) {
                        if(i+1 < sdp[s].size()){
                            res += (ll)pdp[t][i] * (ll)sdp[s][i+1];
                        }
                        if(i){
                            res += (ll)pdp[s][i-1] * (ll)sdp[t][i];
                        }
                    }
                }
                else{
                    for (int i = 0; i < pdp[t].size(); ++i) {
                        if(i){
                            res += (ll)pdp[t][i] * (ll)sdp[s][i-1];
                        }
                        if(i+1 < pdp[s].size()){
                            res += (ll)pdp[s][i+1] * (ll)sdp[t][i];
                        }
                    }
                }
                for (int i = 0; i < pdp[t].size(); ++i) {
                    pdp[s][i] += pdp[t][i];
                    sdp[s][i] += sdp[t][i];
                }
            }
            if(str[s] == '('){
                pdp[s].push_front(0);
                sdp[s].pop_front();
                sdp[s].push_back(0);
                sdp[s].push_back(0);
                pdp[s][1]++;
            }
            else{
                sdp[s].push_front(0);
                pdp[s].pop_front();
                pdp[s].push_back(0);
                pdp[s].push_back(0);
                sdp[s][1]++;
            }
            res += pdp[s][0] + sdp[s][0]; // 枝分かれしないパス
        }
//        cout << s+1 << endl;
//        for (int i = 0; i < pdp[s].size(); ++i) {
//            cout << pdp[s][i] << " ";
//        }
//        cout << endl;
//        for (int i = 0; i < sdp[s].size(); ++i) {
//            cout << sdp[s][i] << " ";
//        }
//        cout << endl;
    };
    dfs(dfs, 0, -1);
    cout << res << endl;
}
accuracy: 0.057971014492753624
time_ms: 50
memory_kb: 78928
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=1000000007,MAX=100005,INF=1<<30;

int N;
int bra[MAX];
vector<int> G[MAX];

bool centroid[MAX];
int subtree_size[MAX];

ll ans;

int compute_subtree_size(int u,int p){
    int c=1;
    for(auto a:G[u]){
        if(a==p||centroid[a]) continue;
        c+=compute_subtree_size(a,u);
    }
    return subtree_size[u]=c;
}

pair<int,int> search_centroid(int u,int p,int t){
    pair<int,int> res={INF,-1};
    int s=1,m=0;
    for(auto a:G[u]){
        if(a==p||centroid[a]) continue;
        
        res=min(res,search_centroid(a,u,t));
        
        m=max(m,subtree_size[a]);
        s+=subtree_size[a];
    }
    m=max(m,t-s);
    res=min(res,{m,u});
    return res;
}

void enumerate_paths1(int u,int p,int sum,int mi,vector<int> &ds){
    sum+=bra[u];
    mi+=bra[u];
    chmin(mi,0);
    if(mi==0) ds.push_back(sum);
    for(auto a:G[u]){
        if(a==p||centroid[a]) continue;
        enumerate_paths1(a,u,sum,mi,ds);
    }
}

void enumerate_paths2(int u,int p,int sum,int mi,vector<pair<int,int>> &ds){
    sum+=bra[u];
    chmin(mi,sum);
    if(mi==sum) ds.push_back(mp(sum,mi));
    for(auto a:G[u]){
        if(a==p||centroid[a]) continue;
        enumerate_paths2(a,u,sum,mi,ds);
    }
}

ll count_pairs(vector<int> &ds1,vector<pair<int,int>> &ds2){
    ll res=0;
    map<int,ll> MA1,MA2;
    for(int a:ds1) MA1[a]++;
    for(auto [a,b]:ds2) MA2[a]++;
    for(auto [a,b]:MA1){
        if(MA2.count(-a)) res+=b*MA2[-a];
    }
    return res;
}

void solve_subproblem(int u){
    //cout<<u<<endl;
    compute_subtree_size(u,-1);
    int s=search_centroid(u,-1,subtree_size[u]).second;
    centroid[s]=1;
    
    for(auto a:G[s]){
        if(centroid[a]) continue;
        solve_subproblem(a);
    }
    
    vector<int> ds1;
    vector<pair<int,int>> ds2;
    
    ds1.push_back(0);
    ds2.push_back(mp(bra[s],min(0,bra[s])));
    
    for(auto a:G[s]){
        if(centroid[a]) continue;
        
        vector<int> tds1;
        vector<pair<int,int>> tds2;
        //tds.push_back(mp(bra[s],min(0,bra[s])));
        enumerate_paths1(a,s,0,0,tds1);
        enumerate_paths2(a,s,bra[s],min(0,bra[s]),tds2);
        
        //cout<<"! "<<s<<" "<<a<<endl;
        //for(int x:tds1) cout<<x<<" ";
        //cout<<endl;
        //for(auto [x,y]:tds2) cout<<x<<" "<<y<<" ";
        //cout<<endl;
        
        ans-=count_pairs(tds1,tds2);
        ds1.insert(ds1.end(),all(tds1));
        ds2.insert(ds2.end(),all(tds2));
    }
    
    ans+=count_pairs(ds1,ds2);
    centroid[s]=0;
}

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    cin>>N;
    for(int i=0;i<N;i++){
        char c;cin>>c;
        if(c=='(') bra[i]=1;
        if(c==')') bra[i]=-1;
    }
    for(int i=0;i<N-1;i++){
        int a,b;cin>>a>>b;a--;b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    solve_subproblem(0);
    
    cout<<ans<<endl;
}
accuracy: 1
time_ms: 240
memory_kb: 20036
final_rank: 9

code_snippet: #line 1 "SPJ-Library/tests/centroid_decomposition.test.cpp"
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687"
#include <bits/stdc++.h>

#line 2 "SPJ-Library/graph/centroid_decomposition.hpp"

#line 9 "SPJ-Library/graph/graph.hpp"

#pragma region graph

template <class cost_type = long long>
class graph {
   public:
	struct edge {
	   public:
		int from, to;
		cost_type cost;
		int id;
		edge() = default;
		edge(int from_, int to_, cost_type cost_ = 1, int id_ = -1) : from(from_), to(to_), cost(cost_), id(id_) {}
		bool operator<(const edge &a) const { return cost < a.cost; }
		bool operator>(const edge &a) const { return cost > a.cost; }
		friend std::ostream &operator<<(std::ostream &s, const edge &a) {
			s << '(' << a.from << " -> " << a.to << "), cost: " << a.cost << ", id: " << a.id;
			return s;
		}
	};

   private:
	std::vector<std::vector<edge>> edges;
	int next_edge_id = 0;

   public:
	inline const std::vector<edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<edge> &operator[](int k) { return edges[k]; }

	int size() const { return int(edges.size()); }
	void resize(const int n) { edges.resize(n); }
	int edge_count() const { return next_edge_id; }

	friend std::ostream &operator<<(std::ostream &s, const graph<cost_type> &g) {
		for(const auto &adj : g.edges)
			for(const auto &ed : adj) s << ed << '\n';
		return s;
	}

	graph() = default;
	graph(int n) : edges(n) {}
	graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const cost_type INF = std::numeric_limits<cost_type>::max() / 3;

	void input(int e = -1, bool weight = false, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			std::cin >> u >> v;
			cost_type cost = 1;
			if(weight) std::cin >> cost;
			add_edge(u, v, cost, directed, idx);
		}
	}

	inline int add_edge(int u, int v, cost_type cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(u, v, cost, next_edge_id);
		if(!directed && u != v) edges[v].emplace_back(v, u, cost, next_edge_id);
		return next_edge_id++;
	}

	// Ο(V+E)
	std::vector<cost_type> bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or x (>= 0)
	std::vector<cost_type> zero_one_bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					e.cost ? deq.push_back(e.to) : deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V) lg E)
	// unreachable: INF
	std::vector<cost_type> dijkstra(int s) const {
		std::vector<cost_type> dist(size(), INF);
		const auto compare = [](const std::pair<cost_type, int> &a, const std::pair<cost_type, int> &b) {
			return a.first > b.first;
		};
		std::priority_queue<std::pair<cost_type, int>, std::vector<std::pair<cost_type, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<cost_type, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// unreachable: INF
	// reachable via negative cycle: -INF
	std::vector<cost_type> bellman_ford(int s) const {
		int n = size();
		std::vector<cost_type> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<cost_type>> warshall_floyd() const {
		const int n = size();
		std::vector<std::vector<cost_type>> dist(n, std::vector<cost_type>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a cycle exists, return {}
	std::vector<int> topological_sort() const {
		std::vector<int> res;
		std::vector<int> used(size(), 0);
		bool not_DAG = false;
		auto dfs = [&](auto self, int k) -> void {
			if(not_DAG) return;
			if(used[k]) {
				if(used[k] == 1) not_DAG = true;
				return;
			}
			used[k] = 1;
			for(auto &e : edges[k]) self(self, e.to);
			used[k] = 2;
			res.push_back(k);
		};
		for(int i = 0; i < size(); i++) dfs(dfs, i);
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_dag() const { return !topological_sort().empty(); }

	// Ο(V)
	// array of the distance to the most distant vertex
	// constraint: the graph is a tree
	std::vector<cost_type> height() const {
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		cost_type dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++) {
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		}
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() const {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(colors[i] == -1 and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() const { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// (v1, v2, diameter)
	std::tuple<int, int, cost_type> diameter() {
		std::vector<cost_type> dist = bfs(0);
		auto it = std::max_element(dist.begin(), dist.end());
		const int v = it - dist.begin();
		dist = bfs(v);
		it = std::max_element(dist.begin(), dist.end());
		return std::make_tuple(v, int(it - dist.begin()), *it);
	}

	// Ο(V+E)
	std::vector<int> subtree_size(const int root) {
		const int n = size();
		std::vector<int> ret(n, 1);
		auto dfs = [&](auto self, int now, int p = -1) -> void {
			for(const auto &e : (*this)[now]) {
				if(e.to == p) continue;
				self(self, e.to, now);
				ret[now] += ret[e.to];
			}
		};
		dfs(dfs, root);
		return ret;
	}

	// Ο(ElgE)
	cost_type prim() const {
		cost_type res = 0;
		std::priority_queue<edge, std::vector<edge>, std::greater<edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElgE)
	cost_type kruskal() const {
		std::vector<std::tuple<int, int, cost_type>> eds;
		for(const auto &adj : edges)
			for(const auto &ed : adj) eds.emplace_back(ed.from, ed.to, ed.cost);
		std::sort(eds.begin(), eds.end(), [](const std::tuple<int, int, cost_type> &a, const std::tuple<int, int, cost_type> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		cost_type ret = 0;
		for(auto &e : eds)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() const {
		std::vector<int> centroid, sz(size());
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > size() / 2) is_centroid = false;
				}
			}
			if(size() - sz[now] > size() / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// O(V+E)
	// bridge: (s, t)  (s < t);
	std::pair<std::vector<std::pair<int, int>>, std::vector<int>> bridges_and_articulations() const {
		std::vector<int> order(size(), -1), low(size()), articulation;
		int order_next = 0;
		std::vector<std::pair<int, int>> bridge;
		auto dfs = [&](auto self, int now, int par = -1) -> void {
			low[now] = order[now] = order_next++;
			bool is_articulation = false;
			int cnt = 0;
			for(auto &ed : edges[now]) {
				int &nxt = ed.to;
				if(nxt == par) continue;
				if(order[nxt] == -1) {
					cnt++;
					self(self, nxt, now);
					if(order[now] < low[nxt]) bridge.push_back(std::minmax(now, nxt));
					if(order[now] <= low[nxt]) is_articulation = true;
					low[now] = std::min(low[now], low[nxt]);
				} else if(order[now] > order[nxt]) {
					low[now] = std::min(low[now], order[nxt]);
				}
			}
			if(par == -1 and cnt < 2) is_articulation = false;
			if(is_articulation) articulation.push_back(now);
			return;
		};
		for(int i = 0; i < (int)size(); i++)
			if(order[i] == -1) dfs(dfs, i);
		return std::make_pair(bridge, articulation);
	}

	// Ο(V+E)
	// directed graph from root to leaf
	graph root_to_leaf(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	graph leaf_to_root(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// cost_type Chu_Liu_Edmonds(int root = 0) {}
};
#pragma endregion
#line 4 "SPJ-Library/graph/centroid_decomposition.hpp"

// process(int root, const vector<int>& subtree_size) -> void
// subtree_size[vertex] := 0 if 'vertex' has been removed else (the size of the subtree below 'vertex')
template <class edge_type>
void centroid_decomposition(const graph<edge_type> &g, const std::function<void(int, const std::vector<int> &)> &process) {
	std::vector<int> subtree_size(g.size(), 1);

	auto calc_weight = [&](auto self, int now, int par) -> int {
		assert(subtree_size[now]);
		int &w = (subtree_size[now] = 1);
		for(const auto &ed : g[now]) {
			if(ed.to == par || !subtree_size[ed.to]) continue;
			w += self(self, ed.to, now);
		}
		return w;
	};

	auto find_centroid = [&g, &subtree_size](int now) -> int {
		const int order_half = subtree_size[now] >> 1;
		int par = -1;
		bool changed = true;
		while(std::exchange(changed, false)) {
			for(const auto &ed : g[now]) {
				if(ed.to != par && order_half < subtree_size[ed.to]) {
					par = now;
					now = ed.to;
					changed = true;
					break;
				}
			}
		}
		return now;
	};

	auto decomposite = [&g, &process, &subtree_size, &calc_weight, &find_centroid](auto self, int now) -> void {
		now = find_centroid(now);
		calc_weight(calc_weight, now, -1);
		process(now, subtree_size);
		subtree_size[now] = 0;
		for(const auto &ed : g[now])
			if(subtree_size[ed.to]) self(self, ed.to);
		return;
	};

	for(int i = 0; i < g.size(); i++)
		if(subtree_size[i]) {
			calc_weight(calc_weight, i, -1);
			decomposite(decomposite, i);
		}
	return;
}
#line 5 "SPJ-Library/tests/centroid_decomposition.test.cpp"

int main() {
	using namespace std;
	using ll = long long;
	int n;
	cin >> n;
	vector<int> v(n);
	for(int i = 0; i < n; i++) {
		char c;
		cin >> c;
		v[i] = (c == '(' ? 1 : -1);
	}
	graph<int> g(n, -1);
	ll ret = 0;
	vector<int> up_tmp, dn_tmp;

	auto dfs = [&](int now, int par, const vector<int>& subtree_size, auto self, int minvalue, int sum, int maxvalue) -> void {
		const int nxtsum = sum + v[now];
		minvalue = min(minvalue, nxtsum);
		maxvalue = max(maxvalue, nxtsum);
		for(const auto& e : g[now]) {
			if(e.to == par || !subtree_size[e.to]) continue;
			self(e.to, now, subtree_size, self, minvalue, nxtsum, maxvalue);
		}
		if(nxtsum == minvalue) dn_tmp.push_back(-minvalue);
		if(nxtsum == maxvalue) up_tmp.push_back(nxtsum);
		return;
	};

	auto process = [&](int root, const vector<int>& subtree_size) -> void {
		const int sz = subtree_size[root] + 2;
		vector<int> up(sz), dn(sz);
		up[1] = dn[1] = 1;
		for(const auto& e : g[root]) {
			if(!subtree_size[e.to]) continue;
			dfs(e.to, root, subtree_size, dfs, 0, 0, 0);
			map<int, int> up_counter, dn_counter;
			for(const auto& a : up_tmp) ret += dn[a + 1 + v[root]];
			for(const auto& a : dn_tmp) ret += up[a + 1 - v[root]];
			for(const auto& a : up_tmp) up[a + 1]++;
			for(const auto& a : dn_tmp) dn[a + 1]++;
			up_tmp.clear();
			dn_tmp.clear();
		}
		return;
	};

	centroid_decomposition(g, process);
	cout << ret << '\n';
	return 0;
}
accuracy: 1
time_ms: 150
memory_kb: 19724
final_rank: 5

code_snippet: #line 1 "SPJ-Library/tests/centroid_decomposition.test.cpp"
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687"
#include <bits/stdc++.h>

#line 2 "SPJ-Library/graph/centroid_decomposition.hpp"

#line 9 "SPJ-Library/graph/graph.hpp"

#pragma region graph

template <class cost_type = long long>
class graph {
   public:
	struct edge {
	   public:
		int from, to;
		cost_type cost;
		int id;
		edge() = default;
		edge(int from_, int to_, cost_type cost_ = 1, int id_ = -1) : from(from_), to(to_), cost(cost_), id(id_) {}
		bool operator<(const edge &a) const { return cost < a.cost; }
		bool operator>(const edge &a) const { return cost > a.cost; }
		friend std::ostream &operator<<(std::ostream &s, const edge &a) {
			s << '(' << a.from << " -> " << a.to << "), cost: " << a.cost << ", id: " << a.id;
			return s;
		}
	};

   private:
	std::vector<std::vector<edge>> edges;
	int next_edge_id = 0;

   public:
	inline const std::vector<edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<edge> &operator[](int k) { return edges[k]; }

	int size() const { return int(edges.size()); }
	void resize(const int n) { edges.resize(n); }
	int edge_count() const { return next_edge_id; }

	friend std::ostream &operator<<(std::ostream &s, const graph<cost_type> &g) {
		for(const auto &adj : g.edges)
			for(const auto &ed : adj) s << ed << '\n';
		return s;
	}

	graph() = default;
	graph(int n) : edges(n) {}
	graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const cost_type INF = std::numeric_limits<cost_type>::max() / 3;

	void input(int e = -1, bool weight = false, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			std::cin >> u >> v;
			cost_type cost = 1;
			if(weight) std::cin >> cost;
			add_edge(u, v, cost, directed, idx);
		}
	}

	inline int add_edge(int u, int v, cost_type cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(u, v, cost, next_edge_id);
		if(!directed && u != v) edges[v].emplace_back(v, u, cost, next_edge_id);
		return next_edge_id++;
	}

	// Ο(V+E)
	std::vector<cost_type> bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or x (>= 0)
	std::vector<cost_type> zero_one_bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					e.cost ? deq.push_back(e.to) : deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V) lg E)
	// unreachable: INF
	std::vector<cost_type> dijkstra(int s) const {
		std::vector<cost_type> dist(size(), INF);
		const auto compare = [](const std::pair<cost_type, int> &a, const std::pair<cost_type, int> &b) {
			return a.first > b.first;
		};
		std::priority_queue<std::pair<cost_type, int>, std::vector<std::pair<cost_type, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<cost_type, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// unreachable: INF
	// reachable via negative cycle: -INF
	std::vector<cost_type> bellman_ford(int s) const {
		int n = size();
		std::vector<cost_type> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<cost_type>> warshall_floyd() const {
		const int n = size();
		std::vector<std::vector<cost_type>> dist(n, std::vector<cost_type>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a cycle exists, return {}
	std::vector<int> topological_sort() const {
		std::vector<int> res;
		std::vector<int> used(size(), 0);
		bool not_DAG = false;
		auto dfs = [&](auto self, int k) -> void {
			if(not_DAG) return;
			if(used[k]) {
				if(used[k] == 1) not_DAG = true;
				return;
			}
			used[k] = 1;
			for(auto &e : edges[k]) self(self, e.to);
			used[k] = 2;
			res.push_back(k);
		};
		for(int i = 0; i < size(); i++) dfs(dfs, i);
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_dag() const { return !topological_sort().empty(); }

	// Ο(V)
	// array of the distance to the most distant vertex
	// constraint: the graph is a tree
	std::vector<cost_type> height() const {
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		cost_type dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++) {
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		}
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() const {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(colors[i] == -1 and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() const { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// (v1, v2, diameter)
	std::tuple<int, int, cost_type> diameter() {
		std::vector<cost_type> dist = bfs(0);
		auto it = std::max_element(dist.begin(), dist.end());
		const int v = it - dist.begin();
		dist = bfs(v);
		it = std::max_element(dist.begin(), dist.end());
		return std::make_tuple(v, int(it - dist.begin()), *it);
	}

	// Ο(V+E)
	std::vector<int> subtree_size(const int root) {
		const int n = size();
		std::vector<int> ret(n, 1);
		auto dfs = [&](auto self, int now, int p = -1) -> void {
			for(const auto &e : (*this)[now]) {
				if(e.to == p) continue;
				self(self, e.to, now);
				ret[now] += ret[e.to];
			}
		};
		dfs(dfs, root);
		return ret;
	}

	// Ο(ElgE)
	cost_type prim() const {
		cost_type res = 0;
		std::priority_queue<edge, std::vector<edge>, std::greater<edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElgE)
	cost_type kruskal() const {
		std::vector<std::tuple<int, int, cost_type>> eds;
		for(const auto &adj : edges)
			for(const auto &ed : adj) eds.emplace_back(ed.from, ed.to, ed.cost);
		std::sort(eds.begin(), eds.end(), [](const std::tuple<int, int, cost_type> &a, const std::tuple<int, int, cost_type> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		cost_type ret = 0;
		for(auto &e : eds)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() const {
		std::vector<int> centroid, sz(size());
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > size() / 2) is_centroid = false;
				}
			}
			if(size() - sz[now] > size() / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// O(V+E)
	// bridge: (s, t)  (s < t);
	std::pair<std::vector<std::pair<int, int>>, std::vector<int>> bridges_and_articulations() const {
		std::vector<int> order(size(), -1), low(size()), articulation;
		int order_next = 0;
		std::vector<std::pair<int, int>> bridge;
		auto dfs = [&](auto self, int now, int par = -1) -> void {
			low[now] = order[now] = order_next++;
			bool is_articulation = false;
			int cnt = 0;
			for(auto &ed : edges[now]) {
				int &nxt = ed.to;
				if(nxt == par) continue;
				if(order[nxt] == -1) {
					cnt++;
					self(self, nxt, now);
					if(order[now] < low[nxt]) bridge.push_back(std::minmax(now, nxt));
					if(order[now] <= low[nxt]) is_articulation = true;
					low[now] = std::min(low[now], low[nxt]);
				} else if(order[now] > order[nxt]) {
					low[now] = std::min(low[now], order[nxt]);
				}
			}
			if(par == -1 and cnt < 2) is_articulation = false;
			if(is_articulation) articulation.push_back(now);
			return;
		};
		for(int i = 0; i < (int)size(); i++)
			if(order[i] == -1) dfs(dfs, i);
		return std::make_pair(bridge, articulation);
	}

	// Ο(V+E)
	// directed graph from root to leaf
	graph root_to_leaf(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	graph leaf_to_root(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// cost_type Chu_Liu_Edmonds(int root = 0) {}
};
#pragma endregion
#line 4 "SPJ-Library/graph/centroid_decomposition.hpp"

// process(int root, const vector<int>& subtree_size) -> void
// subtree_size[vertex] := 0 if 'vertex' has been removed else (the size of the subtree below 'vertex')
template <class edge_type>
void centroid_decomposition(const graph<edge_type> &g, const std::function<void(int, const std::vector<int> &)> &process) {
	std::vector<int> subtree_size(g.size(), 1);

	auto calc_weight = [&](auto self, int now, int par) -> int {
		assert(subtree_size[now]);
		int &w = (subtree_size[now] = 1);
		for(const auto &ed : g[now]) {
			if(ed.to == par || !subtree_size[ed.to]) continue;
			w += self(self, ed.to, now);
		}
		return w;
	};

	auto find_centroid = [&g, &calc_weight, &subtree_size](int now) -> int {
		calc_weight(calc_weight, now, -1);
		const int order_half = subtree_size[now] >> 1;
		int par = -1;
		bool changed = true;
		while(std::exchange(changed, false)) {
			for(const auto &ed : g[now]) {
				if(ed.to != par && order_half < subtree_size[ed.to]) {
					par = now;
					now = ed.to;
					changed = true;
					break;
				}
			}
		}
		return now;
	};

	auto decomposite = [&g, &process, &subtree_size, &find_centroid](auto self, int now) -> void {
		now = find_centroid(now);
		process(now, subtree_size);
		subtree_size[now] = 0;
		for(const auto &ed : g[now])
			if(subtree_size[ed.to]) self(self, ed.to);
		return;
	};

	for(int i = 0; i < g.size(); i++)
		if(subtree_size[i]) decomposite(decomposite, i);
	return;
}
#line 5 "SPJ-Library/tests/centroid_decomposition.test.cpp"

int main() {
	using namespace std;
	using ll = long long;
	int n;
	cin >> n;
	vector<int> v(n);
	for(int i = 0; i < n; i++) {
		char c;
		cin >> c;
		v[i] = (c == '(' ? 1 : -1);
	}
	graph<int> g(n, -1);
	ll ret = 0;
	vector<int> up_tmp, dn_tmp;

	auto dfs = [&](int now, int par, const vector<int>& subtree_size, auto self, int minvalue, int sum, int maxvalue) -> void {
		const int nxtsum = sum + v[now];
		minvalue = min(minvalue, nxtsum);
		maxvalue = max(maxvalue, nxtsum);
		for(const auto& e : g[now]) {
			if(e.to == par || !subtree_size[e.to]) continue;
			self(e.to, now, subtree_size, self, minvalue, nxtsum, maxvalue);
		}
		if(nxtsum == minvalue) dn_tmp.push_back(-minvalue);
		if(nxtsum == maxvalue) up_tmp.push_back(nxtsum);
		return;
	};

	auto process = [&](int root, const vector<int>& subtree_size) -> void {
		const int sz = subtree_size[root] + 2;
		vector<int> up(sz), dn(sz);
		up[1] = dn[1] = 1;
		for(const auto& e : g[root]) {
			if(!subtree_size[e.to]) continue;
			dfs(e.to, root, subtree_size, dfs, 0, 0, 0);
			map<int, int> up_counter, dn_counter;
			for(const auto& a : up_tmp) ret += dn[a + 1 + v[root]];
			for(const auto& a : dn_tmp) ret += up[a + 1 - v[root]];
			for(const auto& a : up_tmp) up[a + 1]++;
			for(const auto& a : dn_tmp) dn[a + 1]++;
			up_tmp.clear();
			dn_tmp.clear();
		}
		return;
	};

	centroid_decomposition(g, process);
	cout << ret << '\n';
	return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 19344
final_rank: 4

code_snippet: #line 1 "a.cpp"
#define PROBELM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687"

#line 2 "SPJ-Library/graph/graph.hpp"
#include <algorithm>
#include <cassert>
#include <deque>
#include <iostream>
#include <queue>
#include <tuple>
#include <vector>

#pragma region graph

template <class cost_type = long long>
class graph {
   public:
	struct edge {
	   public:
		int from, to;
		cost_type cost;
		int id;
		edge() = default;
		edge(int from_, int to_, cost_type cost_ = 1, int id_ = -1) : from(from_), to(to_), cost(cost_), id(id_) {}
		bool operator<(const edge &a) const { return cost < a.cost; }
		bool operator>(const edge &a) const { return cost > a.cost; }
		friend std::ostream &operator<<(std::ostream &s, const edge &a) {
			s << '(' << a.from << " -> " << a.to << "), cost: " << a.cost << ", id: " << a.id;
			return s;
		}
	};

   private:
	std::vector<std::vector<edge>> edges;
	int next_edge_id = 0;

   public:
	inline const std::vector<edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<edge> &operator[](int k) { return edges[k]; }

	int size() const { return int(edges.size()); }
	void resize(const int n) { edges.resize(n); }
	int edge_count() const { return next_edge_id; }

	friend std::ostream &operator<<(std::ostream &s, const graph<cost_type> &g) {
		for(const auto &adj : g.edges)
			for(const auto &ed : adj) s << ed << '\n';
		return s;
	}

	graph() = default;
	graph(int n) : edges(n) {}
	graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const cost_type INF = std::numeric_limits<cost_type>::max() / 3;

	void input(int e = -1, bool weight = false, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			std::cin >> u >> v;
			cost_type cost = 1;
			if(weight) std::cin >> cost;
			add_edge(u, v, cost, directed, idx);
		}
	}

	inline int add_edge(int u, int v, cost_type cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(u, v, cost, next_edge_id);
		if(!directed && u != v) edges[v].emplace_back(v, u, cost, next_edge_id);
		return next_edge_id++;
	}

	// Ο(V+E)
	std::vector<cost_type> bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or x (>= 0)
	std::vector<cost_type> zero_one_bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					e.cost ? deq.push_back(e.to) : deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V) lg E)
	// unreachable: INF
	std::vector<cost_type> dijkstra(int s) const {
		std::vector<cost_type> dist(size(), INF);
		const auto compare = [](const std::pair<cost_type, int> &a, const std::pair<cost_type, int> &b) {
			return a.first > b.first;
		};
		std::priority_queue<std::pair<cost_type, int>, std::vector<std::pair<cost_type, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<cost_type, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// unreachable: INF
	// reachable via negative cycle: -INF
	std::vector<cost_type> bellman_ford(int s) const {
		int n = size();
		std::vector<cost_type> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<cost_type>> warshall_floyd() const {
		const int n = size();
		std::vector<std::vector<cost_type>> dist(n, std::vector<cost_type>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a cycle exists, return {}
	std::vector<int> topological_sort() const {
		std::vector<int> res;
		std::vector<int> used(size(), 0);
		bool not_DAG = false;
		auto dfs = [&](auto self, int k) -> void {
			if(not_DAG) return;
			if(used[k]) {
				if(used[k] == 1) not_DAG = true;
				return;
			}
			used[k] = 1;
			for(auto &e : edges[k]) self(self, e.to);
			used[k] = 2;
			res.push_back(k);
		};
		for(int i = 0; i < size(); i++) dfs(dfs, i);
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_dag() const { return !topological_sort().empty(); }

	// Ο(V)
	// array of the distance to the most distant vertex
	// constraint: the graph is a tree
	std::vector<cost_type> height() const {
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		cost_type dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++) {
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		}
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() const {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(!colors[i] and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() const { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// (v1, v2, diameter)
	std::tuple<int, int, cost_type> diameter() {
		std::vector<cost_type> dist = bfs(0);
		auto it = std::max_element(dist.begin(), dist.end());
		const int v = it - dist.begin();
		dist = bfs(v);
		it = std::max_element(dist.begin(), dist.end());
		return std::make_tuple(v, int(it - dist.begin()), *it);
	}

	// Ο(V+E)
	std::vector<int> subtree_size(const int root) {
		const int n = size();
		std::vector<int> ret(n, 1);
		auto dfs = [&](auto self, int now, int p = -1) -> void {
			for(const auto &e : (*this)[now]) {
				if(e.to == p) continue;
				self(self, e.to, now);
				ret[now] += ret[e.to];
			}
		};
		dfs(dfs, root);
		return ret;
	}

	// Ο(ElgE)
	cost_type prim() const {
		cost_type res = 0;
		std::priority_queue<edge, std::vector<edge>, std::greater<edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElgE)
	cost_type kruskal() const {
		std::vector<std::tuple<int, int, cost_type>> eds;
		for(const auto &adj : edges)
			for(const auto &ed : adj) eds.emplace_back(ed.from, ed.to, ed.cost);
		std::sort(eds.begin(), eds.end(), [](const std::tuple<int, int, cost_type> &a, const std::tuple<int, int, cost_type> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		cost_type ret = 0;
		for(auto &e : eds)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() const {
		std::vector<int> centroid, sz(size());
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > size() / 2) is_centroid = false;
				}
			}
			if(size() - sz[now] > size() / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// O(V+E)
	// bridge: (s, t)  (s < t);
	std::pair<std::vector<std::pair<int, int>>, std::vector<int>> bridges_and_articulations() const {
		std::vector<int> order(size(), -1), low(size()), articulation;
		int order_next = 0;
		std::vector<std::pair<int, int>> bridge;
		auto dfs = [&](auto self, int now, int par = -1) -> void {
			low[now] = order[now] = order_next++;
			bool is_articulation = false;
			int cnt = 0;
			for(auto &ed : edges[now]) {
				int &nxt = ed.to;
				if(nxt == par) continue;
				if(order[nxt] == -1) {
					cnt++;
					self(self, nxt, now);
					if(order[now] < low[nxt]) bridge.push_back(std::minmax(now, nxt));
					if(order[now] <= low[nxt]) is_articulation = true;
					low[now] = std::min(low[now], low[nxt]);
				} else if(order[now] > order[nxt]) {
					low[now] = std::min(low[now], order[nxt]);
				}
			}
			if(par == -1 and cnt < 2) is_articulation = false;
			if(is_articulation) articulation.push_back(now);
			return;
		};
		for(int i = 0; i < (int)size(); i++)
			if(order[i] == -1) dfs(dfs, i);
		return std::make_pair(bridge, articulation);
	}

	// Ο(V+E)
	// directed graph from root to leaf
	graph root_to_leaf(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	graph leaf_to_root(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// cost_type Chu_Liu_Edmonds(int root = 0) {}
};
#pragma endregion
#line 2 "SPJ-Library/graph/centroid_decomposition_lambda.hpp"

#include <functional>

// process(int root, const vector<int>& used_as_centroid) -> void
// used_as_centroid[vertex] := 1 if 'vertex' has been removed else 0
template <class edge_type>
void centroid_decomposition(const graph<edge_type> &g, const std::function<void(int, const std::vector<int> &)> &process) {
	std::vector<int> weight(g.size(), 0), used_as_centroid(g.size(), 0);

	auto calc_weight = [&](auto self, int now, int par) -> void {
		int &weight_now = (weight[now] = 1);
		for(const auto &ed : g[now]) {
			if(ed.to == par || used_as_centroid[ed.to]) continue;
			self(self, ed.to, now);
			weight_now += weight[ed.to];
		}
		return;
	};

	auto find_centroid = [&g, &calc_weight, &weight, &used_as_centroid](int now) -> int {
		calc_weight(calc_weight, now, -1);
		const int order_half = weight[now] >> 1;
		int par = -1;
		bool changed = true;
		while(std::exchange(changed, false)) {
			for(const auto &ed : g[now]) {
				if(ed.to != par && !used_as_centroid[ed.to] && weight[ed.to] > order_half) {
					par = now;
					now = ed.to;
					changed = true;
					break;
				}
			}
		}
		return now;
	};

	auto decomposite = [&g, &find_centroid, &used_as_centroid, &process](auto self, int now) -> void {
		now = find_centroid(now);
		process(now, used_as_centroid);
		used_as_centroid[now] = 1;
		for(const auto &ed : g[now])
			if(!used_as_centroid[ed.to]) self(self, ed.to);
		return;
	};

	for(int i = 0; i < g.size(); i++)
		if(!used_as_centroid[i]) decomposite(decomposite, i);
	return;
}
#line 4 "a.cpp"

using namespace std;
#include <bits/stdc++.h>
using ll = long long;

int main() {
	int n;
	cin >> n;
	vector<int> v(n);
	for(int i = 0; i < n; i++) {
		char c;
		cin >> c;
		v[i] = (c == '(' ? 1 : -1);
	}
	graph<int> g(n, -1);
	ll ret = 0;
	vector<int> up_tmp, dn_tmp;

	vector<int> weight(n);
	auto calc_weight = [&](auto self, int now, int par, const vector<int>& used_as_centroid) -> void {
		int& weight_now = (weight[now] = 1);
		for(const auto& ed : g[now]) {
			if(ed.to == par || used_as_centroid[ed.to]) continue;
			self(self, ed.to, now, used_as_centroid);
			weight_now += weight[ed.to];
		}
		return;
	};

	auto dfs = [&](int now, int par, const vector<int>& visited, auto self, int minvalue, int sum, int maxvalue) -> void {
		const int nxtsum = sum + v[now];
		minvalue = min(minvalue, nxtsum);
		maxvalue = max(maxvalue, nxtsum);
		for(const auto& e : g[now]) {
			if(e.to == par || visited[e.to]) continue;
			self(e.to, now, visited, self, minvalue, nxtsum, maxvalue);
		}
		if(nxtsum == minvalue) dn_tmp.push_back(-minvalue);
		if(nxtsum == maxvalue) up_tmp.push_back(nxtsum);
		return;
	};

	auto process = [&](int root, const vector<int>& visited) -> void {
		calc_weight(calc_weight, root, -1, visited);
		const int sz = weight[root] + 2;
		vector<int> up(sz), dn(sz);
		up[1] = dn[1] = 1;
		for(const auto& e : g[root]) {
			if(visited[e.to]) continue;
			dfs(e.to, root, visited, dfs, 0, 0, 0);
			map<int, int> up_counter, dn_counter;
			for(const auto& a : up_tmp) ret += dn[a + 1 + v[root]];
			for(const auto& a : dn_tmp) ret += up[a + 1 - v[root]];
			for(const auto& a : up_tmp) up[a + 1]++;
			for(const auto& a : dn_tmp) dn[a + 1]++;
			up_tmp.clear();
			dn_tmp.clear();
		}
		return;
	};

	centroid_decomposition(g, process);
	cout << ret << '\n';
	return 0;
}
accuracy: 1
time_ms: 180
memory_kb: 22164
final_rank: 7

code_snippet: #line 1 "a.cpp"
#define PROBELM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687"

using namespace std;
#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < n; i++)
using ll = long long;

#line 9 "SPJ-Library/graph/graph.hpp"

#pragma region graph

template <class cost_type = long long>
class graph {
   public:
	struct edge {
	   public:
		int from, to;
		cost_type cost;
		int id;
		edge() = default;
		edge(int from_, int to_, cost_type cost_ = 1, int id_ = -1) : from(from_), to(to_), cost(cost_), id(id_) {}
		bool operator<(const edge &a) const { return cost < a.cost; }
		bool operator>(const edge &a) const { return cost > a.cost; }
		friend std::ostream &operator<<(std::ostream &s, const edge &a) {
			s << '(' << a.from << " -> " << a.to << "), cost: " << a.cost << ", id: " << a.id;
			return s;
		}
	};

   private:
	std::vector<std::vector<edge>> edges;
	int next_edge_id = 0;

   public:
	inline const std::vector<edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<edge> &operator[](int k) { return edges[k]; }

	int size() const { return int(edges.size()); }
	void resize(const int n) { edges.resize(n); }
	int edge_count() const { return next_edge_id; }

	friend std::ostream &operator<<(std::ostream &s, const graph<cost_type> &g) {
		for(const auto &adj : g.edges)
			for(const auto &ed : adj) s << ed << '\n';
		return s;
	}

	graph() = default;
	graph(int n) : edges(n) {}
	graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const cost_type INF = std::numeric_limits<cost_type>::max() / 3;

	void input(int e = -1, bool weight = false, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			std::cin >> u >> v;
			cost_type cost = 1;
			if(weight) std::cin >> cost;
			add_edge(u, v, cost, directed, idx);
		}
	}

	inline int add_edge(int u, int v, cost_type cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(u, v, cost, next_edge_id);
		if(!directed && u != v) edges[v].emplace_back(v, u, cost, next_edge_id);
		return next_edge_id++;
	}

	// Ο(V+E)
	std::vector<cost_type> bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or x (>= 0)
	std::vector<cost_type> zero_one_bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					e.cost ? deq.push_back(e.to) : deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V) lg E)
	// unreachable: INF
	std::vector<cost_type> dijkstra(int s) const {
		std::vector<cost_type> dist(size(), INF);
		const auto compare = [](const std::pair<cost_type, int> &a, const std::pair<cost_type, int> &b) {
			return a.first > b.first;
		};
		std::priority_queue<std::pair<cost_type, int>, std::vector<std::pair<cost_type, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<cost_type, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// unreachable: INF
	// reachable via negative cycle: -INF
	std::vector<cost_type> bellman_ford(int s) const {
		int n = size();
		std::vector<cost_type> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<cost_type>> warshall_floyd() const {
		const int n = size();
		std::vector<std::vector<cost_type>> dist(n, std::vector<cost_type>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a cycle exists, return {}
	std::vector<int> topological_sort() const {
		std::vector<int> res;
		std::vector<int> used(size(), 0);
		bool not_DAG = false;
		auto dfs = [&](auto self, int k) -> void {
			if(not_DAG) return;
			if(used[k]) {
				if(used[k] == 1) not_DAG = true;
				return;
			}
			used[k] = 1;
			for(auto &e : edges[k]) self(self, e.to);
			used[k] = 2;
			res.push_back(k);
		};
		for(int i = 0; i < size(); i++) dfs(dfs, i);
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_dag() const { return !topological_sort().empty(); }

	// Ο(V)
	// array of the distance to the most distant vertex
	// constraint: the graph is a tree
	std::vector<cost_type> height() const {
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		cost_type dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++) {
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		}
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() const {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(!colors[i] and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() const { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// (v1, v2, diameter)
	std::tuple<int, int, cost_type> diameter() {
		std::vector<cost_type> dist = bfs(0);
		auto it = std::max_element(dist.begin(), dist.end());
		const int v = it - dist.begin();
		dist = bfs(v);
		it = std::max_element(dist.begin(), dist.end());
		return std::make_tuple(v, int(it - dist.begin()), *it);
	}

	// Ο(V+E)
	std::vector<int> subtree_size(const int root) {
		const int n = size();
		std::vector<int> ret(n, 1);
		auto dfs = [&](auto self, int now, int p = -1) -> void {
			for(const auto &e : (*this)[now]) {
				if(e.to == p) continue;
				self(self, e.to, now);
				ret[now] += ret[e.to];
			}
		};
		dfs(dfs, root);
		return ret;
	}

	// Ο(ElgE)
	cost_type prim() const {
		cost_type res = 0;
		std::priority_queue<edge, std::vector<edge>, std::greater<edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElgE)
	cost_type kruskal() const {
		std::vector<std::tuple<int, int, cost_type>> eds;
		for(const auto &adj : edges)
			for(const auto &ed : adj) eds.emplace_back(ed.from, ed.to, ed.cost);
		std::sort(eds.begin(), eds.end(), [](const std::tuple<int, int, cost_type> &a, const std::tuple<int, int, cost_type> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		cost_type ret = 0;
		for(auto &e : eds)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() const {
		std::vector<int> centroid, sz(size());
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > size() / 2) is_centroid = false;
				}
			}
			if(size() - sz[now] > size() / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// O(V+E)
	// bridge: (s, t)  (s < t);
	std::pair<std::vector<std::pair<int, int>>, std::vector<int>> bridges_and_articulations() const {
		std::vector<int> order(size(), -1), low(size()), articulation;
		int order_next = 0;
		std::vector<std::pair<int, int>> bridge;
		auto dfs = [&](auto self, int now, int par = -1) -> void {
			low[now] = order[now] = order_next++;
			bool is_articulation = false;
			int cnt = 0;
			for(auto &ed : edges[now]) {
				int &nxt = ed.to;
				if(nxt == par) continue;
				if(order[nxt] == -1) {
					cnt++;
					self(self, nxt, now);
					if(order[now] < low[nxt]) bridge.push_back(std::minmax(now, nxt));
					if(order[now] <= low[nxt]) is_articulation = true;
					low[now] = std::min(low[now], low[nxt]);
				} else if(order[now] > order[nxt]) {
					low[now] = std::min(low[now], order[nxt]);
				}
			}
			if(par == -1 and cnt < 2) is_articulation = false;
			if(is_articulation) articulation.push_back(now);
			return;
		};
		for(int i = 0; i < (int)size(); i++)
			if(order[i] == -1) dfs(dfs, i);
		return std::make_pair(bridge, articulation);
	}

	// Ο(V+E)
	// directed graph from root to leaf
	graph root_to_leaf(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	graph leaf_to_root(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// cost_type Chu_Liu_Edmonds(int root = 0) {}
};
#pragma endregion
#line 2 "SPJ-Library/graph/centroid_decomposition_lambda.hpp"

// process(int root, const vector<int>& used_as_centroid) -> void
// used_as_centroid[vertex] := 1 if 'vertex' has been removed else 0
template <class edge_type>
void centroid_decomposition(const graph<edge_type> &g, const std::function<void(int, const vector<int> &)> &process) {
	std::vector<int> weight(g.size(), 0), used_as_centroid(g.size(), 0);

	auto calc_weight = [&](auto self, int now, int par) -> void {
		int &weight_now = (weight[now] = 1);
		for(const auto &ed : g[now]) {
			if(ed.to == par || used_as_centroid[ed.to]) continue;
			self(self, ed.to, now);
			weight_now += weight[ed.to];
		}
		return;
	};

	auto find_centroid = [&g, &calc_weight, &weight, &used_as_centroid](int now) -> int {
		calc_weight(calc_weight, now, -1);
		const int order_half = weight[now] >> 1;
		int par = -1;
		bool changed = true;
		while(std::exchange(changed, false)) {
			for(const auto &ed : g[now]) {
				if(ed.to != par && !used_as_centroid[ed.to] && weight[ed.to] > order_half) {
					par = now;
					now = ed.to;
					changed = true;
					break;
				}
			}
		}
		return now;
	};

	auto decomposite = [&g, &find_centroid, &used_as_centroid, &process](auto self, int now) -> void {
		now = find_centroid(now);
		process(now, used_as_centroid);
		used_as_centroid[now] = 1;
		for(const auto &ed : g[now])
			if(!used_as_centroid[ed.to]) self(self, ed.to);
		return;
	};

	for(int i = 0; i < g.size(); i++)
		if(!used_as_centroid[i]) decomposite(decomposite, i);
	return;
}
#line 9 "a.cpp"



#pragma region debug

#pragma region output
template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(int i = 0; i < (int)v.size(); i++) {
		if(i) os << " ";
		os << v[i];
	}
	return os;
}
#pragma endregion

#pragma region view

template <typename T>
void view(const T e) {
	std::cerr << e;
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << "(";
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ")";
}

template <typename T>
void view(const std::set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<T> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	std::cerr << "{\n";
	for(const auto &v : vv) {
		std::cerr << "\t";
		view(v);
		std::cerr << "\n";
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	std::cerr << "{\n";
	for(const auto &c : v) {
		std::cerr << "\t(";
		view(c.first);
		std::cerr << ", ";
		view(c.second);
		std::cerr << ")\n";
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << "\n";
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << "\n";
	}
	std::cerr << "}";
}

#pragma endregion

// when debugging : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(0)
#define dump(x)                                      \
	do {                                             \
		std::cerr << __LINE__ << " " << #x << " : "; \
		view(x);                                     \
		std::cerr << "\n";                           \
	} while(0)

#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

#pragma endregion



int main() {
	int n;
	cin >> n;
	vector<int> v(n);
	rep(i, n) {
		char c;
		cin >> c;
		v[i] = (c == '(' ? 1 : -1);
	}
	graph<int> g(n, -1);
	ll ret = 0;
	vector<int> up_tmp, dn_tmp;

	auto dfs = [&](int now, int par, const vector<int>& visited, auto self, int minvalue, int sum, int maxvalue) -> void {
		const int nxtsum = sum + v[now];
		minvalue = min(minvalue, nxtsum);
		maxvalue = max(maxvalue, nxtsum);
		for(const auto& e : g[now]) {
			if(e.to == par || visited[e.to]) continue;
			self(e.to, now, visited, self, minvalue, nxtsum, maxvalue);
		}
		if(nxtsum == minvalue) {
			dn_tmp.push_back(-minvalue);
		}
		if(nxtsum == maxvalue) {
			up_tmp.push_back(nxtsum);
		}
		return;
	};

	auto process = [&](int root, const vector<int>& visited) -> void {
		map<int, int> up, dn;
		up[0]++;
		dn[0]++;
		for(const auto& e : g[root]) {
			if(visited[e.to]) continue;
			dfs(e.to, root, visited, dfs, 0, 0, 0);
			map<int, int> up_counter, dn_counter;
			for(const auto& a : up_tmp) {
				up_counter[a]++;
				up[a]++;
			}
			for(const auto& a : dn_tmp) {
				dn_counter[a]++;
				dn[a]++;
			}
			debug(root, e.to);
			debug(up_counter);
			debug(dn_counter);
			for(const auto& p : up_counter) {
				ret -= ll(p.second) * dn_counter[p.first + v[root]];
			}
			up_tmp.clear();
			dn_tmp.clear();
		}
		for(const auto& p : up) {
			ret += ll(p.second) * dn[p.first + v[root]];
		}
		return;
	};

	centroid_decomposition(g, process);

	cout << ret << '\n';

	return 0;
}
accuracy: 1
time_ms: 290
memory_kb: 21460
final_rank: 12

code_snippet: #line 1 "a.cpp"
#define PROBELM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687"

using namespace std;
#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < n; i++)
using ll = long long;

#line 9 "SPJ-Library/graph/graph.hpp"

#pragma region graph

template <class cost_type = long long>
class graph {
   public:
	struct edge {
	   public:
		int from, to;
		cost_type cost;
		int id;
		edge() = default;
		edge(int from_, int to_, cost_type cost_ = 1, int id_ = -1) : from(from_), to(to_), cost(cost_), id(id_) {}
		bool operator<(const edge &a) const { return cost < a.cost; }
		bool operator>(const edge &a) const { return cost > a.cost; }
		friend std::ostream &operator<<(std::ostream &s, const edge &a) {
			s << '(' << a.from << " -> " << a.to << "), cost: " << a.cost << ", id: " << a.id;
			return s;
		}
	};

   private:
	std::vector<std::vector<edge>> edges;
	int next_edge_id = 0;

   public:
	inline const std::vector<edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<edge> &operator[](int k) { return edges[k]; }

	int size() const { return int(edges.size()); }
	void resize(const int n) { edges.resize(n); }
	int edge_count() const { return next_edge_id; }

	friend std::ostream &operator<<(std::ostream &s, const graph<cost_type> &g) {
		for(const auto &adj : g.edges)
			for(const auto &ed : adj) s << ed << '\n';
		return s;
	}

	graph() = default;
	graph(int n) : edges(n) {}
	graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const cost_type INF = std::numeric_limits<cost_type>::max() / 3;

	void input(int e = -1, bool weight = false, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			std::cin >> u >> v;
			cost_type cost = 1;
			if(weight) std::cin >> cost;
			add_edge(u, v, cost, directed, idx);
		}
	}

	inline int add_edge(int u, int v, cost_type cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(u, v, cost, next_edge_id);
		if(!directed && u != v) edges[v].emplace_back(v, u, cost, next_edge_id);
		return next_edge_id++;
	}

	// Ο(V+E)
	std::vector<cost_type> bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or x (>= 0)
	std::vector<cost_type> zero_one_bfs(int s) const {
		std::vector<cost_type> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					e.cost ? deq.push_back(e.to) : deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V) lg E)
	// unreachable: INF
	std::vector<cost_type> dijkstra(int s) const {
		std::vector<cost_type> dist(size(), INF);
		const auto compare = [](const std::pair<cost_type, int> &a, const std::pair<cost_type, int> &b) { return a.first > b.first; };
		std::priority_queue<std::pair<cost_type, int>, std::vector<std::pair<cost_type, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<cost_type, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// unreachable: INF
	// reachable via negative cycle: -INF
	std::vector<cost_type> bellman_ford(int s) const {
		int n = size();
		std::vector<cost_type> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<cost_type>> warshall_floyd() const {
		const int n = size();
		std::vector<std::vector<cost_type>> dist(n, std::vector<cost_type>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a cycle exists, return {}
	std::vector<int> topological_sort() const {
		std::vector<int> res;
		std::vector<int> used(size(), 0);
		bool not_DAG = false;
		auto dfs = [&](auto self, int k) -> void {
			if(not_DAG) return;
			if(used[k]) {
				if(used[k] == 1) not_DAG = true;
				return;
			}
			used[k] = 1;
			for(auto &e : edges[k]) self(self, e.to);
			used[k] = 2;
			res.push_back(k);
		};
		for(int i = 0; i < size(); i++) dfs(dfs, i);
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_dag() const { return !topological_sort().empty(); }

	// Ο(V)
	// array of the distance to the most distant vertex
	// constraint: the graph is a tree
	std::vector<cost_type> height() const {
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		cost_type dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++) {
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		}
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() const {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(!colors[i] and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() const { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// (v1, v2, diameter)
	std::tuple<int, int, cost_type> diameter() {
		std::vector<cost_type> dist = bfs(0);
		auto it = std::max_element(dist.begin(), dist.end());
		const int v = it - dist.begin();
		dist = bfs(v);
		it = std::max_element(dist.begin(), dist.end());
		return std::make_tuple(v, int(it - dist.begin()), *it);
	}

	// Ο(V+E)
	std::vector<int> subtree_size(const int root) {
		const int n = size();
		std::vector<int> ret(n, 1);
		auto dfs = [&](auto self, int now, int p = -1) -> void {
			for(const auto &e : (*this)[now]) {
				if(e.to == p) continue;
				self(self, e.to, now);
				ret[now] += ret[e.to];
			}
		};
		dfs(dfs, root);
		return ret;
	}

	// Ο(ElgE)
	cost_type prim() const {
		cost_type res = 0;
		std::priority_queue<edge, std::vector<edge>, std::greater<edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElgE)
	cost_type kruskal() const {
		std::vector<std::tuple<int, int, cost_type>> eds;
		for(const auto &adj : edges)
			for(const auto &ed : adj) eds.emplace_back(ed.from, ed.to, ed.cost);
		std::sort(eds.begin(), eds.end(), [](const std::tuple<int, int, cost_type> &a, const std::tuple<int, int, cost_type> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		cost_type ret = 0;
		for(auto &e : eds)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() const {
		std::vector<int> centroid, sz(size());
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > size() / 2) is_centroid = false;
				}
			}
			if(size() - sz[now] > size() / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// O(V+E)
	// bridge: (s, t)  (s < t);
	std::pair<std::vector<std::pair<int, int>>, std::vector<int>> bridges_and_articulations() const {
		std::vector<int> order(size(), -1), low(size()), articulation;
		int order_next = 0;
		std::vector<std::pair<int, int>> bridge;
		auto dfs = [&](auto self, int now, int par = -1) -> void {
			low[now] = order[now] = order_next++;
			bool is_articulation = false;
			int cnt = 0;
			for(auto &ed : edges[now]) {
				int &nxt = ed.to;
				if(nxt == par) continue;
				if(order[nxt] == -1) {
					cnt++;
					self(self, nxt, now);
					if(order[now] < low[nxt]) bridge.push_back(std::minmax(now, nxt));
					if(order[now] <= low[nxt]) is_articulation = true;
					low[now] = std::min(low[now], low[nxt]);
				} else if(order[now] > order[nxt]) {
					low[now] = std::min(low[now], order[nxt]);
				}
			}
			if(par == -1 and cnt < 2) is_articulation = false;
			if(is_articulation) articulation.push_back(now);
			return;
		};
		for(int i = 0; i < (int)size(); i++)
			if(order[i] == -1) dfs(dfs, i);
		return std::make_pair(bridge, articulation);
	}

	// Ο(V+E)
	// directed graph from root to leaf
	graph root_to_leaf(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	graph leaf_to_root(int root = 0) const {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// cost_type Chu_Liu_Edmonds(int root = 0) {}
};
#pragma endregion
#line 2 "SPJ-Library/graph/centroid_decomposition_lambda.hpp"

// process(int root, const vector<int>& used_as_centroid) -> void
// used_as_centroid[vertex] := 1 if 'vertex' has been removed else 0
template <class edge_type>
void centroid_decomposition(const graph<edge_type> &g, const std::function<void(int, const vector<int> &)> &process) {
	std::vector<int> weight(g.size(), 0), used_as_centroid(g.size(), 0);

	auto calc_weight = [&](auto self, int now, int par) -> void {
		int &weight_now = (weight[now] = 1);
		for(const auto &ed : g[now]) {
			if(ed.to == par || used_as_centroid[ed.to]) continue;
			self(self, ed.to, now);
			weight_now += weight[ed.to];
		}
		return;
	};

	auto find_centroid = [&g, &calc_weight, &weight, &used_as_centroid](int now) -> int {
		calc_weight(calc_weight, now, -1);
		const int order_half = weight[now] >> 1;
		int par = -1;
		bool changed = true;
		while(std::exchange(changed, false)) {
			for(const auto &ed : g[now]) {
				if(ed.to != par && !used_as_centroid[ed.to] && weight[ed.to] > order_half) {
					par = now;
					now = ed.to;
					changed = true;
					break;
				}
			}
		}
		return now;
	};

	auto decomposite = [&g, &find_centroid, &used_as_centroid, &process](auto self, int now) -> void {
		now = find_centroid(now);
		process(now, used_as_centroid);
		used_as_centroid[now] = 1;
		for(const auto &ed : g[now])
			if(!used_as_centroid[ed.to]) self(self, ed.to);
		return;
	};

	for(int i = 0; i < g.size(); i++)
		if(!used_as_centroid[i]) decomposite(decomposite, i);
	return;
}
#line 9 "a.cpp"

int main() {
	int n;
	cin >> n;
	vector<int> v(n);
	rep(i, n) {
		char c;
		cin >> c;
		v[i] = (c == '(' ? 1 : -1);
	}
	graph<int> g(n, -1);
	ll ret = 0;
	vector<int> up_tmp, dn_tmp;

	auto dfs = [&](int now, int par, const vector<int> &visited, auto self, int minvalue, int sum, int maxvalue) -> void {
		const int nxtsum = sum + v[now];
		minvalue = min(minvalue, nxtsum);
		maxvalue = max(maxvalue, nxtsum);
		for(const auto &e : g[now]) {
			if(e.to == par || visited[e.to]) continue;
			self(e.to, now, visited, self, minvalue, nxtsum, maxvalue);
		}
		if(nxtsum == minvalue) {
			dn_tmp.push_back(-minvalue);
		}
		if(nxtsum == maxvalue) {
			up_tmp.push_back(nxtsum);
		}
		return;
	};

	auto process = [&](int root, const vector<int> &visited) -> void {
		map<int, int> up, dn;
		up[0]++;
		dn[0]++;
		for(const auto &e : g[root]) {
			if(visited[e.to]) continue;
			dfs(e.to, root, visited, dfs, 0, 0, 0);
			map<int, int> up_counter, dn_counter;
			for(const auto &a : up_tmp) {
				up_counter[a]++;
				up[a]++;
			}
			for(const auto &a : dn_tmp) {
				dn_counter[a]++;
				dn[a]++;
			}
			for(const auto &p : up_counter) {
				ret -= ll(p.second) * dn_counter[p.first + v[root]];
			}
			up_tmp.clear();
			dn_tmp.clear();
		}
		for(const auto &p : up) {
			ret += ll(p.second) * dn[p.first + v[root]];
		}
		return;
	};

	centroid_decomposition(g, process);

	cout << ret << '\n';

	return 0;
}
accuracy: 1
time_ms: 270
memory_kb: 21424
final_rank: 10

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
#include<chrono>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;

template<typename T>
void chmin(T& a, T b) {
	a = min(a, b);
}
template<typename T>
void chmax(T& a, T b) {
	a = max(a, b);
}
template<typename T>
void cinarray(vector<T>& v) {
	rep(i, v.size())cin >> v[i];
}
template<typename T>
void coutarray(vector<T>& v) {
	rep(i, v.size()) {
		if (i > 0)cout << " "; cout << v[i];
	}
	cout << "\n";
}
ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	if (x == 0)return 0;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	int n;
	modint() :n(0) { ; }
	modint(ll m) {
		if (m < 0 || mod <= m) {
			m %= mod; if (m < 0)m += mod;
		}
		n = m;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 10;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	a = abs(a); b = abs(b);
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

using mP = pair<modint, modint>;
//-----------------------------------

struct Data {
	int ad[2];
	map<int, int> mp[2];
};
void solve() {
	int n; cin >> n;
	string s; cin >> s;
	vector<vector<int>> G(n);
	rep(i, n - 1) {
		int a, b; cin >> a >> b; a--; b--;
		G[a].push_back(b);
		G[b].push_back(a);
	}
	ll ans = 0;
	function<Data(int, int)>dfs=[&](int id, int fr)->Data {
		assert(0 <= id && id < s.size());
		Data res;
		rep(j, 2) {
			res.ad[j] = 0;
		}
		if (s[id] == '(') {
			res.mp[0][1] = 1;
		}
		else {
			res.mp[1][1] = 1;
		}
		for (int to : G[id])if (to != fr) {
			Data nex = dfs(to, id);
			//add answer
			rep(j, 2) {
				bool sw = false;
				if (res.mp[j^1].size() > nex.mp[j].size()) {
					swap(res.mp[j^1], nex.mp[j]);
					swap(res.ad[j^1], nex.ad[j]);
					sw = true;
				}
				for (P p : res.mp[j^1]) {
					int loc = p.first + res.ad[j^1] - nex.ad[j];
					ans += (ll)p.second * nex.mp[j][loc];
					/*if (id == 1) {
						cout << j<<" "<<sw << " " << p.first+res.ad[j^1]<<" "<<p.second << " " << nex.mp[j][loc] << "\n";
					}*/
				}
				if (sw) {
					swap(res.mp[j^1], nex.mp[j]);
					swap(res.ad[j^1], nex.ad[j]);
				}
			}
			//merge
			//update
			if (s[id] == '(') {
				nex.ad[0] += 1;
				nex.ad[1] -= 1;
			}
			else {
				nex.ad[0] -= 1;
				nex.ad[1] += 1;
			}
			rep(j, 2) {
				while (nex.mp[j].size() && (*nex.mp[j].begin()).first + nex.ad[j] < 0) {
					nex.mp[j].erase(nex.mp[j].begin());
				}
			}
			rep(j, 2) {
				if (res.mp[j].size() < nex.mp[j].size()) {
					swap(res.mp[j], nex.mp[j]);
					swap(res.ad[j], nex.ad[j]);
				}
				for (P p : nex.mp[j]) {
					int loc = p.first + nex.ad[j] - res.ad[j];
					//cout << "? " << id << " " << loc << "\n";
					res.mp[j][loc] += p.second;
				}
			}
		}
		//cout << id << " " << res.mp[1][1 - res.ad[1]] << "\n";
		//cout << id << " " << ans << "\n";
		return res;
	};
	dfs(0,-1);
	cout << ans << "\n";
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//while(true)
	//expr();
	//int t; cin >> t; rep(i, t)
	//while(cin>>n,n)
	solve();
	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 46044
final_rank: 2

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
#include<chrono>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;

template<typename T>
void chmin(T& a, T b) {
	a = min(a, b);
}
template<typename T>
void chmax(T& a, T b) {
	a = max(a, b);
}
template<typename T>
void cinarray(vector<T>& v) {
	rep(i, v.size())cin >> v[i];
}
template<typename T>
void coutarray(vector<T>& v) {
	rep(i, v.size()) {
		if (i > 0)cout << " "; cout << v[i];
	}
	cout << "\n";
}
ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	if (x == 0)return 0;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	int n;
	modint() :n(0) { ; }
	modint(ll m) {
		if (m < 0 || mod <= m) {
			m %= mod; if (m < 0)m += mod;
		}
		n = m;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 10;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	a = abs(a); b = abs(b);
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

using mP = pair<modint, modint>;
//-----------------------------------

struct Data {
	int ad[2];
	map<int, int> mp[2];
};
void solve() {
	int n; cin >> n;
	string s; cin >> s;
	vector<vector<int>> G(n);
	rep(i, n - 1) {
		int a, b; cin >> a >> b; a--; b--;
		G[a].push_back(b);
		G[b].push_back(a);
	}
	ll ans = 0;
	function<Data(int, int)>dfs=[&](int id, int fr)->Data {
		assert(0 <= id && id < s.size());
		Data res;
		rep(j, 2) {
			res.ad[j] = 0;
		}
		if (s[id] == '(') {
			res.mp[0][1] = 1;
		}
		else {
			res.mp[1][1] = 1;
		}
		for (int to : G[id])if (to != fr) {
			Data nex = dfs(to, id);
			//add answer
			rep(j, 2) {
				bool sw = false;
				if (res.mp[j^1].size() > nex.mp[j].size()) {
					swap(res.mp[j^1], nex.mp[j]);
					swap(res.ad[j^1], nex.ad[j]);
					sw = true;
				}
				for (P p : res.mp[j^1]) {
					int loc = p.first + res.ad[j^1] - nex.ad[j];
					ans += (ll)p.second * nex.mp[j][loc];
				}
				if (sw) {
					swap(res.mp[j^1], nex.mp[j]);
					swap(res.ad[j^1], nex.ad[j]);
				}
			}
			//merge
			//update
			if (s[id] == '(') {
				nex.ad[0] += 1;
				nex.ad[1] -= 1;
			}
			else {
				nex.ad[0] -= 1;
				nex.ad[1] += 1;
			}
			rep(j, 2) {
				while (nex.mp[j].size() && (*nex.mp[j].begin()).first + nex.ad[j] < 0) {
					nex.mp[j].erase(nex.mp[j].begin());
				}
			}
			rep(j, 2) {
				if (res.mp[j].size() < nex.mp[j].size()) {
					swap(res.mp[j], nex.mp[j]);
				}
				for (P p : nex.mp[j]) {
					int loc = p.first + nex.ad[j] - res.ad[j];
					res.mp[j][loc] += p.second;
				}
			}
		}
		return res;
	};
	dfs(0,-1);
	cout << ans << "\n";
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//while(true)
	//expr();
	//int t; cin >> t; rep(i, t)
	//while(cin>>n,n)
	solve();
	return 0;
}
accuracy: 0.057971014492753624
time_ms: 10
memory_kb: 5880
final_rank: 17

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
#include<chrono>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;

template<typename T>
void chmin(T& a, T b) {
	a = min(a, b);
}
template<typename T>
void chmax(T& a, T b) {
	a = max(a, b);
}
template<typename T>
void cinarray(vector<T>& v) {
	rep(i, v.size())cin >> v[i];
}
template<typename T>
void coutarray(vector<T>& v) {
	rep(i, v.size()) {
		if (i > 0)cout << " "; cout << v[i];
	}
	cout << "\n";
}
ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	if (x == 0)return 0;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	int n;
	modint() :n(0) { ; }
	modint(ll m) {
		if (m < 0 || mod <= m) {
			m %= mod; if (m < 0)m += mod;
		}
		n = m;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 10;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	a = abs(a); b = abs(b);
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

using mP = pair<modint, modint>;
//-----------------------------------

struct Data {
	int ad[2];
	map<int, int> mp[2];
};
void solve() {
	int n; cin >> n;
	string s; cin >> s;
	vector<vector<int>> G(n);
	rep(i, n - 1) {
		int a, b; cin >> a >> b; a--; b--;
		G[a].push_back(b);
		G[b].push_back(a);
	}
	ll ans = 0;
	function<Data(int, int)>dfs=[&](int id, int fr)->Data {
		Data res;
		rep(j, 2) {
			res.ad[j] = 0;
		}
		if (s[id] == '(') {
			res.mp[0][1] = 1;
		}
		else {
			res.mp[1][1] = 1;
		}
		for (int to : G[id])if (to != fr) {
			Data nex = dfs(to, id);
			//add answer
			rep(j, 2) {
				bool sw = false;
				if (res.mp[j^1].size() > nex.mp[j].size()) {
					swap(res.mp[j^1], nex.mp[j]);
					swap(res.ad[j^1], nex.ad[j]);
					sw = true;
				}
				for (P p : res.mp[j^1]) {
					int loc = p.first + res.ad[j^1] - nex.ad[j];
					ans += (ll)p.second * nex.mp[j][loc];
				}
				if (sw) {
					swap(res.mp[j^1], nex.mp[j]);
					swap(res.ad[j^1], nex.ad[j]);
				}
			}
			//merge
			//update
			if (s[id] == '(') {
				nex.ad[0] += 1;
				nex.ad[1] -= 1;
			}
			else {
				nex.ad[0] -= 1;
				nex.ad[1] += 1;
			}
			rep(j, 2) {
				while (nex.mp[j].size() && (*nex.mp[j].begin()).first + nex.ad[j] < 0) {
					nex.mp[j].erase(nex.mp[j].begin());
				}
			}
			rep(j, 2) {
				if (res.mp[j].size() < nex.mp[j].size()) {
					swap(res.mp[j], nex.mp[j]);
				}
				for (P p : nex.mp[j]) {
					int loc = p.first + nex.ad[j] - res.ad[j];
					res.mp[j][loc] += p.second;
				}
			}
		}
		return res;
	};
	dfs(0,-1);
	cout << ans << "\n";
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(10);
	//init_f();
	//init();
	//while(true)
	//expr();
	//int t; cin >> t; rep(i, t)
	//while(cin>>n,n)
	solve();
	return 0;
}
accuracy: 0.057971014492753624
time_ms: 10
memory_kb: 5960
final_rank: 18

code_snippet: // verification-helper: PROBLEM http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687

#include <bits/stdc++.h>
using namespace std;

#define call_from_test
template<typename F>
struct FixPoint : F{
  FixPoint(F&& f):F(forward<F>(f)){}
  template<typename... Args>
  decltype(auto) operator()(Args&&... args) const{
    return F::operator()(*this,forward<Args>(args)...);
  }
};
template<typename F>
inline decltype(auto) MFP(F&& f){
  return FixPoint<F>{forward<F>(f)};
}

// no resize() because it is dangerous
template<typename T>
struct Rotcev{
  vector<T> data;

  size_t size()const{return data.size();};
  bool empty()const{return data.empty();}

  T& operator[](size_t n){return data[size()-1-n];}
  const T& operator[](size_t n)const{return data[size()-1-n];}

  void push_front(T val){data.push_back(val);}
  void pop_front(){data.pop_back();};
  void clear(){data.clear();}

  template<typename... Args>
  Rotcev(Args ...args):data(forward<Args>(args)...){}
  template<typename... Args>
  void emplace_front(Args ...args){data.emplace_back(forward<Args>(args)...);}
  template<typename... Args>
  void assign(Args ...args){data.assign(forward<Args>(args)...);}
};

#undef call_from_test

signed main(){
  cin.tie(0);
  ios::sync_with_stdio(0);

  int n;
  cin>>n;

  string s;
  cin>>s;

  vector< vector<int> > G(n);
  for(int i=1;i<n;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    G[x].emplace_back(y);
    G[y].emplace_back(x);
  }

  using ll = long long;
  ll ans=0;
  vector< Rotcev<int> > dp1(n),dp2(n);
  MFP([&](auto dfs,int v,int p)->void{
      int idx=v;
      for(int u:G[v]){
        if(u==p) continue;
        dfs(u,v);
        if(dp1[idx].size()+dp2[idx].size()<
           dp1[u].size()+dp2[u].size()) idx=u;
      }
      auto &dx=dp1[v];
      auto &dy=dp2[v];

      if(idx!=v){
        for(int u:G[v]){
          if(u==p) continue;
          if(u==idx) continue;
          auto &py=dp2[u];
          int sy=py.size();
          if(s[v]=='('){
            for(int i=0;i+1<sy;i++)
              if(i<(int)dp1[idx].size())
                ans+=(ll)dp1[idx][i]*py[i+1];
          }else{
            for(int i=0;i<sy;i++)
              if(i+1<(int)dp1[idx].size())
                ans+=(ll)dp1[idx][i+1]*py[i];
          }
        }
        swap(dx,dp1[idx]);
        swap(dy,dp2[idx]);
      }else{
        dx.assign(1,0);
        dy.assign(1,0);
      }

      for(int u:G[v]){
        if(u==p) continue;
        if(u==idx) continue;
        auto &px=dp1[u];
        auto &py=dp2[u];
        int sx=px.size();
        int sy=py.size();

        if((int)dx.size()<sx){
          Rotcev<int> tmp(dx);
          dx.assign(sx,0);
          for(int j=0;j<(int)tmp.size();j++) dx[j]+=tmp[j];
        }
        if((int)dy.size()<sy){
          Rotcev<int> tmp(dy);
          dy.assign(sy,0);
          for(int j=0;j<(int)tmp.size();j++) dy[j]+=tmp[j];
        }

        for(int j=0;j<sx;j++) dx[j]+=px[j];
        for(int j=0;j<sy;j++) dy[j]+=py[j];
      }

      // top
      if(s[v]=='('){
        if(1<dy.size()) ans+=dy[1];
      }else{
        if(1<dx.size()) ans+=dx[1];
      }

      //lca
      for(int u:G[v]){
        if(u==p) continue;
        if(u==idx) continue;
        auto &px=dp1[u];
        auto &py=dp2[u];
        int sx=px.size();
        int sy=py.size();
        if(s[v]=='('){
          for(int i=0;i<sx;i++){
            if(i+1<(int)dy.size())
              ans+=(ll)px[i]*dy[i+1];
            if(i+1<sy)
              ans-=(ll)px[i]*py[i+1];
          }
        }else{
          for(int i=0;i+1<sx;i++){
            if(i<(int)dy.size())
              ans+=(ll)px[i+1]*dy[i];
            if(i<sy)
              ans-=(ll)px[i+1]*py[i];
          }
        }
      }

      for(int u:G[v]){
        if(u==p) continue;
        dp1[u].clear();
        dp2[u].clear();
      }

      if(dx.empty()) dx.assign(1,0);
      if(dy.empty()) dy.assign(1,0);
      dx[0]+=1;
      dy[0]+=1;

      if(s[v]=='('){
        dx.emplace_front(0);
        dy.pop_front();
      }else{
        dx.pop_front();
        dy.emplace_front(0);
      }
    })(0,-1);

  cout<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 31912
final_rank: 1

code_snippet: // verification-helper: PROBLEM http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2687

#include <bits/stdc++.h>
using namespace std;

#define call_from_test
template<typename F>
struct FixPoint : F{
  FixPoint(F&& f):F(forward<F>(f)){}
  template<typename... Args>
  decltype(auto) operator()(Args&&... args) const{
    return F::operator()(*this,forward<Args>(args)...);
  }
};
template<typename F>
inline decltype(auto) MFP(F&& f){
  return FixPoint<F>{forward<F>(f)};
}

template<typename T>
struct Rotcev{
  vector<T> data;
  Rotcev(size_t n=0,T val=T()):data(n,val){}


  size_t size()const{return data.size();};
  bool empty()const{return data.empty();}

  T& operator[](size_t k){return data[size()-(k+1)];}

  void push_front(T val){data.push_back(val);}
  void pop_front(){data.pop_back();};
  void clear(){data.clear();}

  template<typename... Args>
  void emplace_front(Args ...args){data.emplace_back(forward<Args>(args)...);}
  template<typename... Args>
  void resize(Args ...args){data.resize(forward<Args>(args)...);}
  template<typename... Args>
  void assign(Args ...args){data.assign(forward<Args>(args)...);}
};

#undef call_from_test

signed main(){
  cin.tie(0);
  ios::sync_with_stdio(0);

  int n;
  cin>>n;

  string s;
  cin>>s;

  vector< vector<int> > G(n);
  for(int i=1;i<n;i++){
    int x,y;
    cin>>x>>y;
    x--;y--;
    G[x].emplace_back(y);
    G[y].emplace_back(x);
  }

  using ll = long long;
  ll ans=0;
  vector< Rotcev<int> > dp1(n),dp2(n);
  MFP([&](auto dfs,int v,int p)->void{
      int idx=v;
      for(int u:G[v]){
        if(u==p) continue;
        dfs(u,v);
        if(dp1[idx].size()+dp2[idx].size()<
           dp1[u].size()+dp2[u].size()) idx=u;
      }
      auto &dx=dp1[v];
      auto &dy=dp2[v];

      if(idx!=v){
        for(int u:G[v]){
          if(u==p) continue;
          if(u==idx) continue;
          auto &py=dp2[u];
          int sy=py.size();
          if(s[v]=='('){
            for(int i=0;i+1<sy;i++){
              if(i<(int)dp1[idx].size())
                ans+=(ll)dp1[idx][i]*py[i+1];
            }
          }else{
            for(int i=0;i<sy;i++){
              if(i+1<(int)dp1[idx].size())
                ans+=(ll)dp1[idx][i+1]*py[i];
            }
          }
        }
        swap(dx,dp1[idx]);
        swap(dy,dp2[idx]);
      }else{
        dx.assign(1,0);
        dy.assign(1,0);
      }

      for(int u:G[v]){
        if(u==p) continue;
        if(u==idx) continue;
        auto &px=dp1[u];
        auto &py=dp2[u];
        int sx=px.size();
        int sy=py.size();

        if((int)dx.size()<sx) dx.resize(sx,0);
        if((int)dy.size()<sy) dy.resize(sy,0);

        for(int j=0;j<sx;j++) dx[j]+=px[j];
        for(int j=0;j<sy;j++) dy[j]+=py[j];
      }

      // top
      if(s[v]=='('){
        if(1<dy.size()) ans+=dy[1];
      }else{
        if(1<dx.size()) ans+=dx[1];
      }

      //lca
      for(int u:G[v]){
        if(u==p) continue;
        if(u==idx) continue;
        auto &px=dp1[u];
        auto &py=dp2[u];
        int sx=px.size();
        int sy=py.size();
        if(s[v]=='('){
          for(int i=0;i<sx;i++){
            if(i+1<(int)dy.size())
              ans+=(ll)px[i]*dy[i+1];
            if(i+1<sy)
              ans-=(ll)px[i]*py[i+1];
          }
        }else{
          for(int i=0;i+1<sx;i++){
            if(i<(int)dy.size())
              ans+=(ll)px[i+1]*dy[i];
            if(i<sy)
              ans-=(ll)px[i+1]*py[i];
          }
        }
      }

      for(int u:G[v]){
        if(u==p) continue;
        dp1[u].clear();
        dp2[u].clear();
      }

      if(dx.empty()) dx.assign(1,0);
      if(dy.empty()) dy.assign(1,0);
      dx[0]+=1;
      dy[0]+=1;

      if(s[v]=='('){
        dx.emplace_front(0);
        dy.pop_front();
      }else{
        dx.pop_front();
        dy.emplace_front(0);
      }
    })(0,-1);

  cout<<ans<<endl;
  return 0;
}
accuracy: 0.057971014492753624
time_ms: 10
memory_kb: 9876
final_rank: 19