// generated at caterpillow.github.io/byot

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

struct Lazy {
    // set, then reverse, then flip
    int set;
    bool rev;
    bool flip;

    void operator+=(const Lazy& oth) {
        if (oth.set != -1) rev = flip = 0, set = oth.set;
        rev ^= oth.rev;
        flip ^= oth.flip;
    }
};

const Lazy lid = {-1, false, false};

// You can implement your own monoid here for custom operations.
struct Value {
    int ans, tot0, tot1, pfx0, pfx1, sfx0, sfx1;

    static Value make(int x, int len = 1) {
        if (x) return {len, 0, len, 0, len, 0, len};
        else return {len, len, 0, len, 0, len, 0};
    } 

    void upd(Lazy lazy, int sz) {
        if (lazy.set != -1) {
            *this = make(lazy.set, tot0 + tot1);
        }
        if (lazy.rev) {
            swap(pfx0, sfx0);
            swap(pfx1, sfx1);
        }
        if (lazy.flip) {
            swap(tot0, tot1);
            swap(pfx0, pfx1);
            swap(sfx0, sfx1);
        }
    }

    Value operator+(const Value& oth) const {
        Value res {};
        res.ans = max({ans, oth.ans, sfx0 + oth.pfx0, sfx1 + oth.pfx1});
        res.tot0 = tot0 + oth.tot0;
        res.tot1 = tot1 + oth.tot1;
        if (tot1 == 0) res.pfx0 = tot0 + oth.pfx0;
        else res.pfx0 = pfx0;
        if (tot0 == 0) res.pfx1 = tot1 + oth.pfx1;
        else res.pfx1 = pfx1;
        if (oth.tot1 == 0) res.sfx0 = sfx0 + oth.tot0;
        else res.sfx0 = oth.sfx0;
        if (oth.tot0 == 0) res.sfx1 = sfx1 + oth.tot1;
        else res.sfx1 = oth.sfx1;
        return res;
    }
};

const Value vid = {0, 0, 0, 0, 0, 0, 0};

// end custom code

// generated at caterpillow.github.io/byot

mt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
using ptr = struct Node*;

struct Node {
    Value val;
    Value agg;
    Lazy lazy;

    int sz;
    int pri;
    ptr l, r;

    Node() {
        pri = mt();
        val = vid;
        agg = vid;
        lazy = lid;
        sz = 1;
        l = r = nullptr;
    }

    Node(Value val) : val(val), agg(val) {
        pri = mt();
        lazy = lid;
        sz = 1;
        l = r = nullptr;
    }

    ~Node() {
        delete l;
        delete r;
    }
};

int sz(ptr n) { return n ? n->sz : 0; };
Value agg(ptr n) { return n ? n->agg : vid; }

void push(ptr n) {
    n->val.upd(n->lazy, 1);
    n->agg.upd(n->lazy, sz(n));
    if (n->lazy.rev) swap(n->l, n->r);
    if (n->l) n->l->lazy += n->lazy;
    if (n->r) n->r->lazy += n->lazy;
    n->lazy = lid;
}

ptr pull(ptr n) {
    if (!n) return nullptr;
    if (n->l) push(n->l);
    if (n->r) push(n->r);
    n->sz = sz(n->l) + 1 + sz(n->r);
    n->agg = agg(n->l) + n->val + agg(n->r);
    return n;
}

ptr merge(ptr l, ptr r) {
    if (!l || !r) return l ? l : r;
    push(l), push(r);
    if (l->pri > r->pri) return l->r = merge(l->r, r), pull(l);
    else return r->l = merge(l, r->l), pull(r);
}

// [-inf, i) and [i, inf]
pair<ptr, ptr> spliti(ptr n, int i) {
    if (!n) return {nullptr, nullptr};
    push(n);
    if (i <= sz(n->l)) {
        auto [l, r] = spliti(n->l, i);
        n->l = r;
        return {l, pull(n)};
    } else {
        auto [l, r] = spliti(n->r, i - 1 - sz(n->l));
        n->r = l;
        return {pull(n), r};
    }
}

// you CANNOT use the normal range update for range reverses
void reversei(ptr& n, int lo, int hi) {
    auto [lm, r] = spliti(n, hi);
    auto [l, m] = spliti(lm, lo);
    Lazy upd = lid;
    upd.rev = true;
    if (m) m->lazy += upd;
    n = merge(merge(l, m), r);
}

void updi(ptr n, int lo, int hi, Lazy lazy) {
    if (!n) return;
    push(n);
    if (lo >= n->sz || hi <= 0) return;
    if (lo <= 0 && n->sz <= hi) {
        n->lazy += lazy;
        push(n);
        return;
    }
    if (lo <= sz(n->l) && sz(n->l) < hi) n->val.upd(lazy, 1);
    updi(n->l, lo, hi, lazy);
    updi(n->r, lo - 1 - sz(n->l), hi - 1 - sz(n->l), lazy);
    pull(n);
}

Value queryi(ptr n, int lo, int hi) {
    if (!n || lo >= sz(n) || hi <= 0) return vid;
    push(n);
    if (lo <= 0 && sz(n) <= hi) return n->agg;
    return queryi(n->l, lo, hi) + (lo <= sz(n->l) && sz(n->l) < hi ? n->val : vid) + queryi(n->r, lo - 1 - sz(n->l), hi - 1 - sz(n->l));
}

void heapify(ptr n) {
    if (!n) return;
    ptr mx = n;
    if (n->l && n->l->pri > mx->pri) mx = n->l;
    if (n->r && n->r->pri > mx->pri) mx = n->r;
    if (mx != n) swap(n->pri, mx->pri), heapify(mx);
}

ptr build(vector<ptr>& ns, int l = 0, int r = -69) {
    if (r == -69) r = (int) ns.size() - 1;
    if (l > r) return nullptr;
    if (l == r) return ns[l];
    int m = (l + r) / 2;
    ns[m]->l = build(ns, l, m - 1);
    ns[m]->r = build(ns, m + 1, r);
    heapify(ns[m]);
    return pull(ns[m]);
}

/*

- include range reverse by index (and lazy prop, size, split by index and merge)
- include build (and heapify)
- include range aggregates
- include range updates by index
- include range queries by index

*/

main() {
    cin.tie(0)->sync_with_stdio(0);
    
    int n, q; cin >> n >> q;

    vector<ptr> ns(n);
    for (int i = 0; i < n; i++) {
        char x; cin >> x;
        ns[i] = new Node(Value::make(x - '0'));
    }
    ptr treap = build(ns);

    for (int i = 0; i < q; i++) {
        int t, l, r; cin >> t >> l >> r;
        l--;
        if (t == 1) {
            updi(treap, l, r, {-1, false, true});
        }
        if (t == 2) {
            reversei(treap, l, r);
        }
        if (t == 3) {
            Value res = queryi(treap, l, r);
            updi(treap, l, l + res.tot0, {0, false, false});
            updi(treap, r - res.tot1, r, {1, false, false});
        }
        push(treap); 
        cout << agg(treap).ans << '\n';
    }
    delete treap;
}
