#include <bits/stdc++.h>
using namespace std;
struct node {
  int l, r, lc, rc;
  int dep_a, dep_a_2b, dep_2b_c, dep_2b, dep_a_2b_c, cover;
};
const int N = 200000;
const int root = 0;
struct Segment_Tree {
  int num;
  node tr[2 * N];
  void setRange(int l, int r, int now = root) {
    num = 0;
    build(l, r, now);
  }
  void build(int l, int r, int now) {
    tr[now].l = l;
    tr[now].r = r;
    tr[now].dep_2b_c = tr[now].dep_a = tr[now].dep_2b = tr[now].dep_a_2b =
        tr[now].dep_a_2b_c = 0;
    tr[now].cover = 0;
    if (l < r) {
      int mid = (l + r) >> 1;
      ++num;
      tr[now].lc = num;
      ++num;
      tr[now].rc = num;
      build(l, mid, tr[now].lc);
      build(mid + 1, r, tr[now].rc);
    }
  }
  void pushup(node &f, node &lc, node &rc) {
    f.dep_a = max(lc.dep_a, rc.dep_a);
    f.dep_2b = max(lc.dep_2b, rc.dep_2b);
    f.dep_a_2b = max(max(lc.dep_a_2b, rc.dep_a_2b), lc.dep_a + rc.dep_2b);
    f.dep_2b_c = max(max(lc.dep_2b_c, rc.dep_2b_c), lc.dep_2b + rc.dep_a);
    f.dep_a_2b_c = max(max(lc.dep_a_2b_c, rc.dep_a_2b_c),
                       max(lc.dep_a + rc.dep_2b_c, lc.dep_a_2b + rc.dep_a));
  }
  void lazyupdate(int now, int val) {
    tr[now].cover += val;
    tr[now].dep_a += val;
    tr[now].dep_a_2b -= val;
    tr[now].dep_2b_c -= val;
    tr[now].dep_2b -= 2 * val;
  }
  void pushdown(int now) {
    lazyupdate(tr[now].lc, tr[now].cover);
    lazyupdate(tr[now].rc, tr[now].cover);
    tr[now].cover = 0;
  }
  void update(int l, int r, int val, int now = root) {
    if (tr[now].l == l && tr[now].r == r) {
      lazyupdate(now, val);
      return;
    } else {
      if (tr[now].cover != 0) pushdown(now);
      int mid = (tr[now].l + tr[now].r) >> 1;
      if (r <= mid)
        update(l, r, val, tr[now].lc);
      else if (l > mid)
        update(l, r, val, tr[now].rc);
      else {
        update(l, mid, val, tr[now].lc);
        update(mid + 1, r, val, tr[now].rc);
      }
      pushup(tr[now], tr[tr[now].lc], tr[tr[now].rc]);
    }
  }
  node query(int l, int r, int now = root) {
    if (tr[now].l == l && tr[now].r == r)
      return tr[now];
    else {
      if (tr[now].cover != 0) pushdown(now);
      int mid = (tr[now].l + tr[now].r) >> 1;
      if (r <= mid)
        return query(l, r, tr[now].lc);
      else if (l > mid)
        return query(l, r, tr[now].rc);
      else {
        node f, lc, rc;
        lc = query(l, mid, tr[now].lc);
        rc = query(mid + 1, r, tr[now].rc);
        pushup(f, lc, rc);
        return f;
      }
    }
  }
};
Segment_Tree tr;
bool p[200000];
int main() {
  int n, q;
  char ch[200000];
  scanf("%d%d%s", &n, &q, ch);
  n = (n - 1) * 2;
  tr.setRange(1, n);
  for (int i = 0; i < n; ++i) {
    if (ch[i] == '(') {
      p[i + 1] = true;
      tr.update(i + 1, n, 1);
    } else {
      p[i + 1] = false;
      tr.update(i + 1, n, -1);
    }
  }
  printf("%d\n", tr.query(1, n).dep_a_2b_c);
  while (q--) {
    int s, t;
    scanf("%d%d", &s, &t);
    if (s > t) swap(s, t);
    if (p[s] != p[t]) {
      p[s] = !p[s];
      p[t] = !p[t];
      if (p[s]) {
        tr.update(s, t - 1, 2);
      } else {
        tr.update(s, t - 1, -2);
      }
    }
    printf("%d\n", tr.query(1, n).dep_a_2b_c);
  }
}
