Problem:
文字列ゲーム
あなたは、双子のアイ君とヅー君に文字列を使ったゲームのプログラムをプレゼントしました。このゲームでは、アイ君とヅー君が文字列の中から部分文字列をそれぞれ選び、それらを比較して小さい方を選んだ人に得点が加算されます。二人は競い合い、何度もゲームを行いました。ところが、同じ文字列に対して何度もゲームをしているうちに飽きてしまいました。そこであなたは、文字列が変化するようにプログラムを修正することにしました。
長さ
N
の文字列
U
と
Q
個の命令文が与えられたとき、以下の命令を処理するプログラムを作成せよ。
文字列
U
の指定された範囲にあるすべての文字を、指定された文字に置き換える。
文字列
U
の指定された２つの部分文字列
S
,
T
を辞書順で比較して、それらの大小関係を出力する。
Input
入力は以下の形式で与えられる。
N
U
Q
query
1
query
2
:
query
Q
１行目に文字列の長さ
N
(1 ≤
N
≤ 200000)、２行目に文字列
U
(英小文字のみを含む文字列)が与えられる。３行目に命令の数
Q
(1 ≤
Q
≤ 100000) が与えられる。続く
Q
行に
i
番目の命令
query
i
が与えられる。各
query
i
は、以下のいずれかの形式で与えられる。
set
x
y
z
または
comp
a
b
c
d
set
x
y
z
は文字列
U
の
x
文字目から
y
文字目までを、指定された文字
z
に置き換えることを表す。ただし、1 ≤
x
≤
y
≤
N
であり、
z
は英小文字である。
comp
a
b
c
d
は文字列
U
の
a
文字目から
b
文字目までの部分文字列を
S
、文字列
U
の
c
文字目から
d
文字目までの部分文字列を
T
としたとき、文字列
S
と文字列
T
を辞書順で比較することを表す。ただし、1 ≤
a
≤
b
≤
N
かつ 1 ≤
c
≤
d
≤
N
である。
Output
各 comp 命令について、
S
の方が小さいならば「s」、
T
の方が小さいならば「t」、両者が一致しているならば「e」と１行に出力する。
Sample Input 1
13
aizualgorithm
9
comp 1 1 4 5
comp 2 6 1 5
set 9 12 b
comp 9 9 10 10
comp 5 8 1 4
set 1 10 z
set 11 13 x
comp 8 10 1 5
comp 1 5 1 5
Sample Output 1
s
t
e
t
s
e

Submitted solutions:

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

typedef long long ll;
using namespace std;

const int MOD = 1e9 + 7;

char c[N * 4];

int mult(ll x, ll y) {
    return (x * y) % MOD;
}

int sm(ll x, ll y) {
    return (x + y) % MOD;
}

int P[N], sum_P[N], t[N * 4], psh[N * 4];

void bld(int v, int l, int r) {
    if (l == r) {
        t[v] = 1 + c[l] - 'a';
        return;
    }

    int md = (l + r) / 2;

    bld(v + v, l, md);
    bld(v + v + 1, md + 1, r);

    t[v] = sm(t[v + v], mult(P[md - l + 1], t[v + v + 1]));
}

void push(int v, int tl, int tr) {
    if (psh[v] == 0)
        return;

    t[v] = mult(psh[v], sum_P[tr - tl]);

    psh[v + v] = psh[v + v + 1] = psh[v];

    psh[v] = 0;
}

int intersect(int l, int r, int L, int R) {
    int mx = max(l, L);
    int mn = min(R, r);

    return max(0, mn - mx + 1);
}

int get(int v, int tl, int tr, int l, int r) {
    push(v, tl, tr);

    if (l <= tl && tr <= r)
        return t[v];

    if (r < tl || tr < l)
        return 0;

    int md = (tl + tr) / 2;

    int a = get(v + v, tl, md, l, r);
    int b = get(v + v + 1, md + 1, tr, l, r);

    return sm(a, mult(b, P[intersect(tl, md, l, r)]));
}

void upd(int v, int tl, int tr, int l, int r, char c) {
    push(v, tl, tr);

    if (l <= tl && tr <= r) {
        psh[v] = 1 + c - 'a';

        push(v, tl, tr);

        return;
    }

    if (r < tl || tr < l)
        return;

    int md = (tl + tr) / 2;

    upd(v + v, tl, md, l, r, c);
    upd(v + v + 1, md + 1, tr, l, r, c);

    t[v] = sm(t[v + v], mult(P[md - tl + 1], t[v + v + 1]));
}

int main() {

    sum_P[0] = P[0] = 1;
    for (int i = 1; i < N; i++)
    {
        P[i] = mult(P[i - 1], 31);
        sum_P[i] = sm(sum_P[i - 1], P[i]);
    }

    int n;
    cin >> n;

    for (int i = 1; i <= n; i++)
        cin >> c[i];

    bld(1, 1, n);

    int q;
    cin >> q;

    while (q--) {
        string s;
        cin >> s;
        if (s == "comp") {
            int l1, l2, r1, r2;
            cin >> l1 >> r1 >> l2 >> r2;

            int len = min(r1 - l1 + 1, r2 - l2 + 1);

            int L = 0, R = len;

            while (L < R) {
                int md = (L + R + 1) / 2;

                int hsh1 = get(1, 1, n, l1, l1 + md - 1);
                int hsh2 = get(1, 1, n, l2, l2 + md - 1);

                if (hsh1 == hsh2) {
                    L = md;
                } else R = md - 1;
            }

            if (len == L) {
                if (r2 - l2 == r1 - l1) {
                    cout << "e" << '\n';
                    continue;
                }

                if (r2 - l2 + 1 == len) {
                    cout << "t" << '\n';
                    continue;
                }

                cout << "s" << '\n';
                continue;
            }

            int c1 = get(1, 1, n, l1 + L, l1 + L);
            int c2 = get(1, 1, n, l2 + L, l2 + L);

            if (c1 < c2) {
                cout << "s" << '\n';
            } else {
                cout << "t" << '\n';
            }

        } else {
            int l, r;
            char t;

            cin >> l >> r;
            cin >> t;

            upd(1, 1, n, l, r, t);
        }
    }
}
accuracy: 1
time_ms: 1010
memory_kb: 19720
final_rank: 7

code_snippet: // AOJ #355
// String Game 2025.2.8

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

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

char temp[200005];
string Cins() { // 文字列の入力　スペース以下の文字で入力終了
    char *s = temp;
	do *s = gc();
	while (*s++ > ' ');
	*(s-1) = 0;
    string input(temp); // 文字配列からstringを作成
    return input;
}

bool Cset() { // 文字列の入力　スペース以下の文字で入力終了
    char temp[10];
    char *s = temp;
	do *s = gc();
	while (*s++ > ' ');
    return temp[0] == 's';
}

void Cout(char e) {
	pc(e), pc('\n');
}

const int MOD1 = 1000000007;
const int MOD2 = 1000000009;
const int P = 131;
 
vector<ll> pow1, pow2, geo1, geo2;
 
struct Node {
    int l, r;
    ll hash1, hash2;
    bool lazy;
    int lazyVal;
};
 
vector<Node> segTree;
 
struct QueryRes {
    int len;
    ll hash1, hash2;
};
 
QueryRes combineQuery(const QueryRes &left, const QueryRes &right) {
    QueryRes res;
    res.len = left.len + right.len;
    res.hash1 = ( left.hash1 * pow1[right.len] + right.hash1 ) % MOD1;
    res.hash2 = ( left.hash2 * pow2[right.len] + right.hash2 ) % MOD2;
    return res;
}
 
void buildTree(const string &s, int idx, int l, int r) {
    segTree[idx].l = l;
    segTree[idx].r = r;
    segTree[idx].lazy = false;
    if(l == r){
        int cval = s[l-1] - 'a' + 1;
        segTree[idx].hash1 = cval;
        segTree[idx].hash2 = cval;
        return;
    }
    int mid = (l + r) / 2;
    buildTree(s, idx*2, l, mid);
    buildTree(s, idx*2+1, mid+1, r);
    int rightLen = segTree[idx*2+1].r - segTree[idx*2+1].l + 1;
    segTree[idx].hash1 = ( segTree[idx*2].hash1 * pow1[rightLen] + segTree[idx*2+1].hash1 ) % MOD1;
    segTree[idx].hash2 = ( segTree[idx*2].hash2 * pow2[rightLen] + segTree[idx*2+1].hash2 ) % MOD2;
}
 
void applyLazy(int idx, int val) {
    int len = segTree[idx].r - segTree[idx].l + 1;
    segTree[idx].hash1 = ( (ll) val * geo1[len] ) % MOD1;
    segTree[idx].hash2 = ( (ll) val * geo2[len] ) % MOD2;
    segTree[idx].lazy = true;
    segTree[idx].lazyVal = val;
}
 
void pushDown(int idx) {
    if(segTree[idx].lazy) {
        applyLazy(idx*2, segTree[idx].lazyVal);
        applyLazy(idx*2+1, segTree[idx].lazyVal);
        segTree[idx].lazy = false;
    }
}
 
void updateTree(int idx, int ql, int qr, int val) {
    int l = segTree[idx].l, r = segTree[idx].r;
    if(ql <= l && r <= qr) {
        applyLazy(idx, val);
        return;
    }
    pushDown(idx);
    int mid = (l + r) / 2;
    if(ql <= mid) updateTree(idx*2, ql, qr, val);
    if(qr > mid) updateTree(idx*2+1, ql, qr, val);
    int rightLen = segTree[idx*2+1].r - segTree[idx*2+1].l + 1;
    segTree[idx].hash1 = ( segTree[idx*2].hash1 * pow1[rightLen] + segTree[idx*2+1].hash1 ) % MOD1;
    segTree[idx].hash2 = ( segTree[idx*2].hash2 * pow2[rightLen] + segTree[idx*2+1].hash2 ) % MOD2;
}
 
QueryRes queryTree(int idx, int ql, int qr) {
    int l = segTree[idx].l, r = segTree[idx].r;
    if(ql <= l && r <= qr) {
        QueryRes res;
        res.len = r - l + 1;
        res.hash1 = segTree[idx].hash1;
        res.hash2 = segTree[idx].hash2;
        return res;
    }
    pushDown(idx);
    int mid = (l + r) / 2;
    QueryRes res;
    res.len = 0; res.hash1 = 0; res.hash2 = 0;
    bool started = false;
    if(ql <= mid) {
        QueryRes leftRes = queryTree(idx*2, ql, qr);
        res = leftRes;
        started = true;
    }
    if(qr > mid) {
        QueryRes rightRes = queryTree(idx*2+1, ql, qr);
        if(!started) res = rightRes;
        else res = combineQuery(res, rightRes);
    }
    return res;
}
 
int getCharVal(int pos) {
    QueryRes qr = queryTree(1, pos, pos);
    return (int)qr.hash1;
}
 
int main(){
    int N = Cin();
    string U = Cins();
    int Q = Cin();
 
    pow1.resize(N+1); pow2.resize(N+1);
    geo1.resize(N+1); geo2.resize(N+1);
    pow1[0] = 1; pow2[0] = 1;
    for (int i = 1; i <= N; i++){
        pow1[i] = (pow1[i-1] * P) % MOD1;
        pow2[i] = (pow2[i-1] * P) % MOD2;
    }
    geo1[0] = 0; geo2[0] = 0;
    for (int i = 1; i <= N; i++){
        geo1[i] = (geo1[i-1] + pow1[i-1]) % MOD1;
        geo2[i] = (geo2[i-1] + pow2[i-1]) % MOD2;
    }
 
    segTree.resize(4 * N + 10);
    buildTree(U, 1, 1, N);
 
    for (int qi = 0; qi < Q; qi++){
        bool cmd = Cset();
        if (cmd){
            int x = Cin(), y = Cin();
            int val = gc() - 'a' + 1; gc();
            updateTree(1, x, y, val);
        } else {
            int a = Cin(), b = Cin(), c = Cin(), d = Cin();
            int lenS = b - a + 1;
            int lenT = d - c + 1;
            int L = min(lenS, lenT);
 
            int lo = 0, hi = L + 1;
            while (lo + 1 < hi) {
                int mid = (lo + hi) >> 1;
                QueryRes qS = queryTree(1, a, a + mid - 1);
                QueryRes qT = queryTree(1, c, c + mid - 1);
                if(qS.hash1 == qT.hash1 && qS.hash2 == qT.hash2) lo = mid;
                else hi = mid;
            }
 
            if(lo == L){
                if(lenS == lenT) Cout('e');
                else if(lenS < lenT) Cout('s');
                else Cout('t');
            } else {
                int charS = getCharVal(a + lo);
                int charT = getCharVal(c + lo);
                if(charS < charT) Cout('s');
                else Cout('t');
            }
        }
    }
    return 0;
}
accuracy: 1
time_ms: 850
memory_kb: 34712
final_rank: 8

code_snippet: #pragma region Macros

#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,fma,abm,mmx,avx,avx2")

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

#define pb emplace_back
#define int ll
#define endl '\n'

#define sqrt __builtin_sqrtl
#define cbrt __builtin_cbrtl
#define hypot __builtin_hypotl

#define next asdnext
#define prev asdprev

using ll = long long;
using ld = long double;
const ld PI = acosl(-1);
const int INF = 1 << 30;
const ll INFL = 1LL << 61;
const int MOD = 998244353;

const ld EPS = 1e-10;
const bool equals(ld a, ld b) { return fabs((a) - (b)) < EPS; }

chrono::system_clock::time_point  start, now;
__attribute__((constructor))
void constructor() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(10);
    start = chrono::system_clock::now();
}

__int128_t POW(__int128_t x, int n) {
    __int128_t ret = 1;
    assert(n >= 0);
    if (x == 1 or n == 0) ret = 1;
    else if (x == -1 && n % 2 == 0) ret = 1; 
    else if (x == -1) ret = -1; 
    else if (n % 2 == 0) {
        assert(x < INFL);
        ret = POW(x * x, n / 2);
    } else {
        assert(x < INFL);
        ret = x * POW(x, n - 1);
    }
    return ret;
}
int per(int x, int y) { // x = qy + r (0 <= r < y) を満たすq
    assert(y != 0);
    if (x >= 0 && y > 0) return x / y;
    if (x >= 0 && y < 0) return x / y - (x % y < 0);
    if (x < 0 && y < 0) return x / y + (x % y < 0);
    return x / y - (x % y < 0); //  (x < 0 && y > 0) 
}
int mod(int x, int y) { // x = qy + r (0 <= r < y) を満たすr
    assert(y != 0);
    if (x >= 0) return x % y;
    __int128_t ret = x % y; // (x < 0)
    ret += (__int128_t)abs(y) * INFL;
    ret %= abs(y);
    return ret;
}
int floor(int x, int y) { // (ld)x / y 以下の最大の整数
    assert(y != 0);
    if (y < 0) x = -x, y = -y;
    return x >= 0 ? x / y : (x + 1) / y - 1;
}
int ceil(int x, int y) { // (ld)x / y 以上の最小の整数
    assert(y != 0);
    if (y < 0) x = -x, y = -y;
    return x > 0 ? (x - 1) / y + 1 : x / y;
}

pair<int, int> max(const pair<int, int> &a, const pair<int, int> &b) {
    if (a.first > b.first or a.first == b.first && a.second > b.second) return a;
    return b;
}
pair<int, int> min(const pair<int, int> &a, const pair<int, int> &b) {
    if (a.first < b.first or a.first == b.first && a.second < b.second) return a;
    return b;
}


int countl_zero(int x) { return __builtin_clzll(x); }
int countl_one(int x) {
    int ret = 0; while (x % 2) { x /= 2; ret++; } return ret;
}
int countr_zero(int x) { return __builtin_ctzll(x); }
int countr_one(int x) {
    int ret = 0, k = 63 - __builtin_clzll(x);
    while (k != -1 && (x & (1LL << k))) { k--; ret++; }
    return ret;
}
int popcount(int x) { return __builtin_popcountll(x); }
int unpopcount(int x) { return 64 - __builtin_clzll(x) - __builtin_popcountll(x); }

int top_bit(int x) { return 63 - __builtin_clzll(x);} // 2^kの位
int bot_bit(int x) { return __builtin_ctz(x);} // 2^kの位
int MSB(int x) { return 1 << (63 - __builtin_clzll(x)); } // mask
int LSB(int x) { return (x & -x); } // mask

int bit_width(int x) { return 64 - __builtin_clzll(x); } // 桁数
int ceil_log2(int x) { return 63 - __builtin_clzll(x); }
int bit_floor(int x) { return 1 << (63 - __builtin_clzll(x)); }
int floor_log2(int x) { return 64 - __builtin_clzll(x-1); }
int bit_ceil(int x) { return 1 << (64 - __builtin_clzll(x-1)) - (x==1); }

int hamming(int a, int b) { return popcount(a ^ b); }
int compcnt(int x) { return (popcount(x^(x >> 1)) + (x&1)) / 2; }


template<int mod> class Modint {
public:
    int val = 0;
    Modint(int x = 0) { while (x < 0) x += mod; val = x % mod; }
    Modint(const Modint &r) { val = r.val; }

    Modint operator -() { return Modint(-val); } // 単項
    Modint operator +(const Modint &r) { return Modint(*this) += r; }
    Modint operator +(const int &q) { Modint r(q); return Modint(*this) += r; }
    Modint operator -(const Modint &r) { return Modint(*this) -= r; }
    Modint operator -(const int &q) { Modint r(q); return Modint(*this) -= r; }
    Modint operator *(const Modint &r) { return Modint(*this) *= r; }
    Modint operator *(const int &q) { Modint r(q); return Modint(*this) *= r; }
    Modint operator /(const Modint &r) { return Modint(*this) /= r; }
    Modint operator /(const int &q) { Modint r(q); return Modint(*this) /= r; }
    
    Modint& operator ++() { val++; if (val >= mod) val -= mod; return *this; } // 前置
    Modint operator ++(signed) { ++*this; return *this; } // 後置
    Modint& operator --() { val--; if (val < 0) val += mod; return *this; }
    Modint operator --(signed) { --*this; return *this; }
    Modint &operator +=(const Modint &r) { val += r.val; if (val >= mod) val -= mod; return *this; }
    Modint &operator +=(const int &q) { Modint r(q); val += r.val; if (val >= mod) val -= mod; return *this; }
    Modint &operator -=(const Modint &r) { if (val < r.val) val += mod; val -= r.val; return *this; }
    Modint &operator -=(const int &q) { Modint r(q);  if (val < r.val) val += mod; val -= r.val; return *this; }
    Modint &operator *=(const Modint &r) { val = val * r.val % mod; return *this; }
    Modint &operator *=(const int &q) { Modint r(q); val = val * r.val % mod; return *this; }
    Modint &operator /=(const Modint &r) {
        int a = r.val, b = mod, u = 1, v = 0;
        while (b) {int t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v);}
        val = val * u % mod; if (val < 0) val += mod;
        return *this;
    }
    Modint &operator /=(const int &q) {
        Modint r(q); int a = r.val, b = mod, u = 1, v = 0;
        while (b) {int t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v);}
        val = val * u % mod; if (val < 0) val += mod;
        return *this;
    }
    bool operator ==(const Modint& r) { return this -> val == r.val; }
    bool operator <(const Modint& r) { return this -> val < r.val; }
    bool operator >(const Modint& r) { return this -> val > r.val; }
    bool operator !=(const Modint& r) { return this -> val != r.val; }
};

using mint = Modint<MOD>;
// using Mint = modint998244353;

istream &operator >>(istream &is, mint& x) {
    int t; is >> t; x = t; return (is);
}
ostream &operator <<(ostream &os, const mint& x) {
    return os << x.val;
}
mint modpow(const mint &x, int n) {
    if (n < 0) return (mint)1 / modpow(x, -n); // 未verify
    assert(n >= 0);
    if (n == 0) return 1;
    mint t = modpow(x, n / 2);
    t = t * t;
    if (n & 1) t = t * x;
    return t;
}

int modpow(__int128_t x, int n, int mod) {
    assert(n >= 0 && mod > 0); // TODO: n <= -1
    __int128_t ret = 1;
    while (n > 0) {
        if (n % 2 == 1) ret = ret * x % mod;
        x = x * x % mod;
        n /= 2;
    }
    return ret;
}
int modinv(__int128_t x, int mod) {
    assert(mod > 0 && x > 0);
    if (x == 1) return 1;
    return mod - modinv(mod % x, mod) * (mod / x) % mod;
}

istream &operator >>(istream &is, __int128_t& x) {
    string S; is >> S;
    __int128_t ret = 0;
    int f = 1;
    if (S[0] == '-') f = -1; 
    for (int i = 0; i < S.length(); i++)
        if ('0' <= S[i] && S[i] <= '9')
            ret = ret * 10 + S[i] - '0';
    x = ret * f;
    return (is);
}
ostream &operator <<(ostream &os, __int128_t x) {
    ostream::sentry s(os);
    if (s) {
        __uint128_t tmp = x < 0 ? -x : x;
        char buffer[128]; char *d = end(buffer);
        do {
            --d; *d = "0123456789"[tmp % 10]; tmp /= 10;
        } while (tmp != 0);
        if (x < 0) { --d; *d = '-'; }
        int len = end(buffer) - d;
        if (os.rdbuf()->sputn(d, len) != len) os.setstate(ios_base::badbit);
    }
    return os;
}

__int128_t stoll(string &S) {
    __int128_t ret = 0; int f = 1;
    if (S[0] == '-') f = -1; 
    for (int i = 0; i < S.length(); i++)
        if ('0' <= S[i] && S[i] <= '9') ret = ret * 10 + S[i] - '0';
    return ret * f;
}
__int128_t gcd(__int128_t a, __int128_t b) { return b ? gcd(b, a % b) : a; }
__int128_t lcm(__int128_t a, __int128_t b) {
    return a / gcd(a, b) * b;
    // lcmが__int128_tに収まる必要あり
}


#pragma endregion

unsigned _bit_ceil(unsigned n) {
    unsigned x = 1;
    while (x < (unsigned)(n)) x *= 2;
    return x;
}

int _countr_zero(unsigned n) {
    return __builtin_ctz(n);
}

template <class S,
          S (*op)(S, S),
          S (*e)(),
          class F,
          S (*mapping)(F, S),
          F (*composition)(F, F),
          F (*id)()>
struct lazy_segtree {
public:
    lazy_segtree() : lazy_segtree(0) {}
    explicit lazy_segtree(int n) : lazy_segtree(vector<S>(n, e())) {}
    explicit lazy_segtree(const vector<S>& v) : _n(int(v.size())) {
        size = (int)_bit_ceil((unsigned)(_n));
        log = _countr_zero((unsigned)size);
        d = vector<S>(2 * size, e());
        lz = vector<F>(size, id());
        for (int i = 0; i < _n; i++) d[size + i] = v[i];
        for (int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }

    void set(int p, S x) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        d[p] = x;
        for (int i = 1; i <= log; i++) update(p >> i);
    }

    S get(int p) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        return d[p];
    }

    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        if (l == r) return e();

        l += size;
        r += size;

        for (int i = log; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push((r - 1) >> i);
        }

        S sml = e(), smr = e();
        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1;
            r >>= 1;
        }

        return op(sml, smr);
    }

    S all_prod() { return d[1]; }

    void apply(int p, F f) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        d[p] = mapping(f, d[p]);
        for (int i = 1; i <= log; i++) update(p >> i);
    }
    void apply(int l, int r, F f) {
        assert(0 <= l && l <= r && r <= _n);
        if (l == r) return;

        l += size;
        r += size;

        for (int i = log; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push((r - 1) >> i);
        }

        {
            int l2 = l, r2 = r;
            while (l < r) {
                if (l & 1) all_apply(l++, f);
                if (r & 1) all_apply(--r, f);
                l >>= 1;
                r >>= 1;
            }
            l = l2;
            r = r2;
        }

        for (int i = 1; i <= log; i++) {
            if (((l >> i) << i) != l) update(l >> i);
            if (((r >> i) << i) != r) update((r - 1) >> i);
        }
    }

    template <bool (*g)(S)> int max_right(int l) {
        return max_right(l, [](S x) { return g(x); });
    }
    template <class G> int max_right(int l, G g) {
        assert(0 <= l && l <= _n);
        assert(g(e()));
        if (l == _n) return _n;
        l += size;
        for (int i = log; i >= 1; i--) push(l >> i);
        S sm = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!g(op(sm, d[l]))) {
                while (l < size) {
                    push(l);
                    l = (2 * l);
                    if (g(op(sm, d[l]))) {
                        sm = op(sm, d[l]);
                        l++;
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l]);
            l++;
        } while ((l & -l) != l);
        return _n;
    }

    template <bool (*g)(S)> int min_left(int r) {
        return min_left(r, [](S x) { return g(x); });
    }
    template <class G> int min_left(int r, G g) {
        assert(0 <= r && r <= _n);
        assert(g(e()));
        if (r == 0) return 0;
        r += size;
        for (int i = log; i >= 1; i--) push((r - 1) >> i);
        S sm = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!g(op(d[r], sm))) {
                while (r < size) {
                    push(r);
                    r = (2 * r + 1);
                    if (g(op(d[r], sm))) {
                        sm = op(d[r], sm);
                        r--;
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }

private:
    int _n, size, log;
    vector<S> d;
    vector<F> lz;

    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
    void all_apply(int k, F f) {
        d[k] = mapping(f, d[k]);
        if (k < size) lz[k] = composition(f, lz[k]);
    }
    void push(int k) {
        all_apply(2 * k, lz[k]);
        all_apply(2 * k + 1, lz[k]);
        lz[k] = id();
    }
};

const int base = 34141;
const int MOD1 = 1919191903;
using Mint = Modint<MOD1>;
vector<Mint> P, sum;

using F = int;
using S = pair<Mint, int>; // 値, 幅
const F ID = 300;
S op(S x, S y) {
    Mint a = x.first * P[y.second] + y.first;
    int b = x.second + y.second;
    return make_pair(a, b);
}
S e() { return make_pair(0, 0); }

S mapping(F f, S x) {
    if (f == ID) return x;
    else return make_pair(sum[x.second]*f, x.second);
}
F composition(F f, F g) { return (f == ID ? g : f); }
F id() { return ID; }

using S2 = long long;
using F2 = long long;

const S2 inf2 = INFL;
const F2 ID2 = INFL;

S2 op2(S2 a, S2 b) { return min(a, b); }
S2 e2() { return inf2; }
S2 mapping2(F2 f, S2 x) { return (f == ID2 ? x : f); }
F2 composition2(F2 f, F2 g) { return (f == ID2 ? g : f); }
F2 id2() { return ID2; }

struct RollingHash {
    int N;
    lazy_segtree<S, op, e, F, mapping, composition, id> H, revH; // ハッシュ
    lazy_segtree<S2, op2, e2, F2, mapping2, composition2, id2> s; // 生文字列

    RollingHash(string &str) : N((int)str.size()) {
        if (P.size() == 0) {
            P.resize(N + 2);
            sum.resize(N + 3);
            P[0] = 1;
            for (int i = 1; i <= N + 1; i++) {
                P[i] = P[i - 1] * base;
            }
            for (int i = 0; i <= N; i++) {
                sum[i + 1] = sum[i] + P[i];
            }
        }

        lazy_segtree<S, op, e, F, mapping, composition, id> h(N + 1), revh(N + 1);
        for (int i = 0; i < N; i++) {
            h.set(i, make_pair(str[i], 1));
            revh.set(i, make_pair(str[N - 1 - i], 1));
        }
        H = h;
        revH = revh;

        lazy_segtree<S2, op2, e2, F2, mapping2, composition2, id2> s2(N);
        for (int i = 0; i < N; i++) {
            s2.set(i, str[i]);
        }
        s = s2;
    }

    Mint get_hash(int l, int r) {
        Mint ret = H.prod(l, r).first;
        return ret;
    }

    bool is_palindrome(int l, int r) {
        Mint h1 = H.prod(l, r).first;
        Mint h2 = revH.prod(N - r, N - l).first;
        if (h1 == h2) return true;
        else return false;
    }

    void set(int l, int r, char c) {
        H.apply(l, r, c);
        revH.apply(N - r, N - l, c);
        s.apply(l, r, c);
    }
    void set(int k, char c) {
        set(k, k + 1, c);
    }

    static int lcp(RollingHash &RS, RollingHash &RT, int ls, int lt) {
        int ok = 0, ng = min(RS.size() - ls, RT.size() - lt) + 1;
        while (ok + 1 < ng) {
            int mid = (ok + ng) / 2;
            (RS.get_hash(ls, mid + ls) == RT.get_hash(lt, mid + lt) ? ok : ng) = mid;
        }
        return ok;
    }

    static int strcmp(RollingHash &RS, RollingHash &RT,
                int ls, int lt, int rs = -1, int rt = -1) {
        if (rs == -1) rs = RS.size();
        if (rt == -1) rt = RT.size();
        int n = min<int>({lcp(RS, RT, ls, lt), rs - ls, rt - lt});

        return ls + n == rs                        ? lt + n == rt ? 0 : -1
             : lt + n == rt                        ? 1
             : RS.s.get(ls + n) < RT.s.get(lt + n) ? -1
                                                   : 1;
    }

    int size() { return N; }
};

signed main() {
    int N, Q;
    string str;
    cin >> N >> str >> Q;

    RollingHash RS(str);

    for (int i = 0; i < Q; i++) {
        string t;
        cin >> t;
        if (t == "set") {
            int l, r;
            char c;
            cin >> l >> r >> c;
            l--; r--;

            RS.set(l, r + 1, c);
        } else {
            int l1, r1, l2, r2;
            cin >> l1 >> r1 >> l2 >> r2;
            l1--; r1--; l2--; r2--;
            int com = RS.strcmp(RS, RS, l1, l2, r1 + 1, r2 + 1);
            if (com == -1) cout << "s" << endl;
            else if (com == 0) cout << "e" << endl;
            else cout << "t" << endl;
        }
    }
}
accuracy: 1
time_ms: 690
memory_kb: 62536
final_rank: 12

code_snippet: #pragma region Template
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vi = vector<int>;
using vl = vector<ll>;
using vvi = vector<vi>;
using vvl = vector<vl>;
#define REP(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define FOR(i, a, b) for (int i = (a), i##_len = (b); i < i##_len; ++i)
#define ALL(x) (x).begin(), (x).end()  // sortなどの引数を省略したい
#ifdef _DEBUG
#define PRE_COMMAND                    \
    cout << fixed << setprecision(15); \
    cin.rdbuf(in.rdbuf()), cout.rdbuf(out.rdbuf());
#else
#define PRE_COMMAND                    \
    cout << fixed << setprecision(15); \
    ios::sync_with_stdio(false), cin.tie(0);
#endif
const double EPS = 1e-10, PI = acos(-1.0);
// 標準入出力
template <size_t... Is, class Tp>
void CIN_TUPLE(istream& is, Tp& t, index_sequence<Is...>) {
    ((is >> get<Is>(t)), ...);
}
template <class... Tp> istream& operator>>(istream& is, tuple<Tp...>& t) {
    CIN_TUPLE(is, t, index_sequence_for<Tp...>());
    return is;
}
template <class T> istream& operator>>(istream& is, vector<T>& vec) {
    for (T& x : vec) is >> x;
    return is;
}
template <size_t... Is, class Tp>
void COUT_TUPLE(ostream& os, const Tp& t, index_sequence<Is...>) {
    ((os << (Is == 0 ? "" : " ") << get<Is>(t)), ...);
}
template <class... Tp> ostream& operator<<(ostream& os, const tuple<Tp...>& t) {
    COUT_TUPLE(os, t, index_sequence_for<Tp...>());
    return os;
}
template <class T> ostream& COUT_MAIN(ostream& os, const T& v) {
    for (auto it = v.begin(); it != v.end(); ++it) {
        os << (it == v.begin() ? "" : " ") << *it;
    }
    return os;
}
template <class T> ostream& operator<<(ostream& os, const vector<T>& v) {
    return COUT_MAIN(os, v);
}
template <class T> ostream& operator<<(ostream& os, const set<T>& v) {
    return COUT_MAIN(os, v);
}
template <class T, class S>
ostream& operator<<(ostream& os, const map<T, S>& m) {
    for (auto it = m.begin(); it != m.end(); ++it) {
        os << (it == m.begin() ? "" : "\n")
           << make_tuple(it->first, it->second);
    }
    return os;
}
// 出力用関数
void PRINT() {}
template <class Head> void PRINT(Head&& head) { cout << head << '\n'; }
template <class Head, class... Tail> void PRINT(Head&& head, Tail&&... tail) {
    cout << head << ' ', PRINT(forward<Tail>(tail)...);
}
template <class T> auto MAX(T& seq) { return *max_element(ALL(seq)); }
template <class T> auto MIN(T& seq) { return *min_element(ALL(seq)); }
template <class T> auto SUM(T& seq) {
    return accumulate(++ALL(seq), *seq.begin());
}
template <class T> void REV(T& seq) { reverse(ALL(seq)); }
template <class T> void SORT(T& seq) { sort(ALL(seq)); }
template <class T, class S> void SORT(T& seq, S ordr) { sort(ALL(seq), ordr); }
template <class T> void SORTR(vector<T>& seq) { sort(ALL(seq), greater<T>()); }
void yn(bool flag) { PRINT(flag ? "Yes" : "No"); }
template <class T> ll pow(ll n, T k) {
    ll now = 1;
    while (k) {
        if (k & 1) now *= n;
        k >>= 1;
        if (k) n *= n;
    }
    return now;
}
template <class T> ll pow(int n, T k) { return pow((ll)n, k); }
template <class T> int pow(int n_0, T k, int mod) {
    n_0 = ((n_0 % mod) + mod) % mod;
    unsigned long long n = (unsigned long long)n_0, now = 1;
    while (k) {
        if (k & 1) now = (now * n) % mod;
        n = (n * n) % mod;
        k >>= 1;
    }
    return (int)now;
}
#pragma endregion Template

template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S),
          F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int n, num, depth = 0;
    std::vector<S> d;
    std::vector<F> lz;
    // 元の配列, op, e, mapping, composition, id
    lazy_segtree(vector<S>& base) { init(base); }
    lazy_segtree() {}
    // 初期化に使用する関数
    void init(vector<S>& base) {
        n = int(base.size());
        while ((1 << depth) < n) depth++;
        num = 1 << depth;
        d = std::vector<S>(num << 1, e());
        lz = std::vector<F>(num, id());
        for (int i = 0; i < n; i++) d[num + i] = base[i];
        for (int i = num - 1; i >= 1; i--) update(i);
    }
    // 頂点kについて、dを下から計算しなおす（上方向に更新）
    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
    // 頂点kについて、dとlzにそれぞれfを作用させる
    void propagate(F f, int k) {
        d[k] = mapping(f, d[k]);
        if (k < num) lz[k] = composition(f, lz[k]);
    }
    // 頂点kについて、lzの中身を一つ下に伝搬させ、lzを単位元にする
    void push(int k) {
        propagate(lz[k], 2 * k);
        propagate(lz[k], 2 * k + 1);
        lz[k] = id();
    }
    // 配列の位置iの値をxにして、根まで順番に上に伝播させる
    void set(S x, int i) {
        assert(0 <= i && i < n);
        i += num;
        for (int j = depth; j >= 1; j--) push(i >> j);
        d[i] = x;
        for (int j = 1; j <= depth; j++) update(i >> j);
    }
    // 配列の[l, r)の区間積を取得する
    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= n);
        // 区間サイズが0なら単位元を出力
        if (l == r) return e();
        l += num, r += num;
        // 区間の大きい方から見て行って、lやrが区間の左端でなければpushして伝播
        for (int i = depth; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push(r >> i);
        }
        S sml = e(), smr = e();
        // 既に必要箇所の伝播が終わっているので、下から順番にopでマージしていく
        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1, r >>= 1;
        }
        return op(sml, smr);
    }
    // 全区間の積を出力
    S all_prod() { return d[1]; }
    // 配列の位置pにfを作用させる
    void apply(F f, int i) {
        assert(0 <= i && i < n);
        i += num;
        // 上から順番にpushで伝播
        for (int j = depth; j >= 1; j--) push(i >> j);
        // pにfを作用させる（d[i]に直接作用させる）
        d[i] = mapping(f, d[i]);
        // 上方向に向かって更新する
        for (int j = 1; j <= depth; j++) update(i >> j);
    }
    // [l, r)にfを作用させる
    void apply(F f, int l, int r) {
        assert(0 <= l && l <= r && r <= n);
        // 区間サイズが0のときは何もしない
        if (l == r) return;
        l += num, r += num;
        for (int i = depth; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            // ACLではupdate((r - 1) >> i)になっているが、同じなはず
            if (((r >> i) << i) != r) push(r >> i);
        }
        int l2 = l, r2 = r;
        while (l < r) {
            // 必要な箇所に下からfを作用させていく
            if (l & 1) propagate(f, l++);
            if (r & 1) propagate(f, --r);
            l >>= 1, r >>= 1;
        }
        l = l2, r = r2;
        // 上方向に向かって更新する
        for (int i = 1; i <= depth; i++) {
            if (((l >> i) << i) != l) update(l >> i);
            // ACLではupdate((r - 1) >> i)になっているが、同じなはず
            if (((r >> i) << i) != r) update(r >> i);
        }
    }
    // op(a[l], a[l+1], ..., a[r-1]) = Trueかつ
    // op(a[l], a[l+1], ..., a[r]) = Falseとなるrを出力、
    // 下式を満たすrがなければnを出力
    template <class G> int max_right(G g, int l = 0) {
        assert(0 <= l && l <= n);
        assert(g(e()));
        if (l == n) return n;
        l += num;
        // 必要な部分について伝播を行っておく
        for (int i = depth; i >= 1; i--) push(l >> i);
        S sm = e();
        // 1が立っているbitが1つになるまで、
        // つまり配列を右端まで探索し終わるまで繰り返す
        do {
            // lを含む区間が右側に来るまで上る
            while (!(l & 1)) l >>= 1;
            // もしダメだったら下に検索していく
            if (!g(op(sm, d[l]))) {
                while (l < num) {
                    push(l);
                    l <<= 1;
                    if (g(op(sm, d[l]))) sm = op(sm, d[l++]);
                }
                return l - num;
            }
            // OKだったら一つ右にずれる、配列の端にいたらl=2^xの形になる
            sm = op(sm, d[l++]);
        } while ((l & -l) != l);
        return n;
    }
    // op(a[l], a[l+1], ..., a[r-1]) = Trueかつ
    // op(a[l-1], a[l], ..., a[r-1]) = Falseとなるlを出力、
    // 下式を満たすlがなければ0を出力
    template <class G> int min_left(G g, int r = -1) {
        if (r == -1) r = n;
        assert(0 <= r && r <= n);
        assert(g(e()));
        if (r == 0) return 0;
        r += num;
        for (int i = depth; i >= 1; i--) push((r - 1) >> i);
        S sm = e();
        do {
            r--;
            while ((r & 1) && r > 1) r >>= 1;
            if (!g(op(d[r], sm))) {
                while (r < num) {
                    push(r);
                    r = (2 * r + 1);
                    if (g(op(d[r], sm))) sm = op(d[r--], sm);
                }
                return r + 1 - num;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }
    // 一点取得：配列の位置iの値を取得する
    S operator[](int i) {
        assert(0 <= i && i < n);
        i += num;
        // 根から順番にpush
        for (int j = depth; j >= 1; j--) push(i >> j);
        // 伝播が終わったので出力
        return d[i];
    }
    // coutによる出力
    friend ostream& operator<<(
        ostream& os, lazy_segtree<S, op, e, F, mapping, composition, id>& s) {
        for (int i = 0; i < s.num; i++) s.push(i);
        if (s.num > 0) os << s.d[s.num];
        for (int i = 1; i < s.n; i++) os << ' ' << s.d[i + s.num];
        return os;
    }
};

// baseの作成
std::random_device rd;
std::mt19937 eng(1);
std::uniform_int_distribution<int> distr(1000000, 999999999);
int base_of_rollinghash = distr(eng);

struct rolling_hash_seg {
    using ul = unsigned long long;
    static const ul mod = (1ULL << 61) - 1;
    static const ul mask31 = (1ULL << 31) - 1, mask30 = (1ULL << 30) - 1;
    static vector<ul> pow_list;
    static ul inv_of_base;
    // 二項演算と単位元
    static const ul ID = 8e18;
    static array<ul, 2> op(array<ul, 2> l, array<ul, 2> r) {
        return {calcmod(l[0] + mul(r[0], pow_list[l[1]])), l[1] + r[1]};
    }
    static array<ul, 2> e() { return {0, 0}; }
    static array<ul, 2> mapping(ul f, array<ul, 2> x) {
        if (f != ID) {
            x[0] = mul(calcmod(1 - pow_list[x[1]] + mod), mul(f, inv_of_base));
        }
        return x;
    }
    static ul composition(ul f, ul g) { return (f == ID ? g : f); }
    static ul id() { return ID; }
    // 本体のセグ木
    lazy_segtree<array<ul, 2>, op, e, ul, mapping, composition, id> seg;
    // 初期化
    rolling_hash_seg(const string& seq)
        : rolling_hash_seg(vector<int>(seq.begin(), seq.end())) {}
    rolling_hash_seg(const vector<int>& seq) {
        int n = (int)seq.size();
        pow_list.assign(n + 1, 1);
        for (int i = 0; i < n; i++) {
            pow_list[i + 1] = mul(pow_list[i], base_of_rollinghash);
        }
        // ここで逆数計算
        ul x = (1 - base_of_rollinghash + mod) % mod, p = mod - 2;
        while (p) {
            if (p & 1) inv_of_base = mul(inv_of_base, x);
            x = mul(x, x);
            p >>= 1;
        }
        vector<array<ul, 2>> v(n);
        for (int i = 0; i < n; i++) v[i] = {(ul)seq[i], 1};
        seg.init(v);
    }
    // 位置iに値xをセット
    void set(int x, int i) { seg.set({ul(x), 1}, i); }
    // 位置iに文字cをセット
    void set(char c, int i) { seg.set({ul(c), 1}, i); }
    // 位置[l, r)に値xをセット
    void set(int x, int l, int r) { seg.apply(ul(x), l, r); }
    // 位置[l, r)に文字cをセット
    void set(char c, int l, int r) { seg.apply(ul(c), l, r); }
    // s[l1:r1]とs[l2:r2]を比較、true or falseで返す
    bool same(int l1, int r1, int l2, int r2) {
        if (r1 - l1 != r2 - l2) return false;
        return calc_hash(l1, r1) == calc_hash(l2, r2);
    }
    // s[l1:r1]とs[l2:r2]を比較、一致→0、前者の方が大→1、後者の方が大→2
    int comp(int l1, int r1, int l2, int r2) {
        int n1 = r1 - l1, n2 = r2 - l2;
        if (same(l1, r1, l2, r2)) return 0;
        if (n1 > n2 and same(l1, l1 + n2, l2, r2)) return 1;
        if (n1 <= n2 and same(l1, r1, l2, l2 + n2)) return 2;
        int ok = 0, ng = min(n1, n2), mid = min(n1, n2) / 2;
        while (abs(ok - ng) > 1) {
            mid = (ok + ng) / 2;
            if (calc_hash(l1, l1 + mid) == calc_hash(l2, l2 + mid)) {
                ok = mid;
            } else {
                ng = mid;
            }
        }
        return seg[l1 + ok][0] > seg[l2 + ok][0] ? 1 : 2;
    }
    // l文字目からr - 1文字目までのハッシュ値
    long long calc_hash(int l, int r) { return (seg.prod(l, r))[0]; }
    // 剰余を計算する
    static ul calcmod(ul x) {
        ul t = (x >> 61) + (x & mod);
        return t - (t >= mod ? mod : 0);
    }
    // 掛け算の関数
    static ul mul(ul a, ul b) {
        ul ad = a & mask31, bd = b & mask31;
        a >>= 31, b >>= 31;
        return calcmod(a * b * 2 + ((a * bd + ad * b) >> 30) +
                       (((a * bd + ad * b) & mask30) << 31) + ad * bd);
    }
    // 一点取得：配列の位置iの値を取得する
    ul operator[](int i) { return seg[i][0]; }
};
vector<unsigned long long> rolling_hash_seg::pow_list;
unsigned long long rolling_hash_seg::inv_of_base = 1;  // 実際は1 - inv

int main() {
    PRE_COMMAND
    int n, q;
    cin >> n;
    string s;
    cin >> s >> q;
    rolling_hash_seg seg(s);
    REP(i, q) {
        string com;
        cin >> com;
        if (com == "comp") {
            int a, b, c, d;
            cin >> a >> b >> c >> d;
            int p = seg.comp(a - 1, b, c - 1, d);
            if (p == 1) {
                PRINT("t");
            } else if (p == 2) {
                PRINT("s");
            } else {
                PRINT("e");
            }

        } else {
            int x, y;
            char c;
            cin >> x >> y >> c;
            seg.set(c, x - 1, y);
        }
    }
}
accuracy: 1
time_ms: 540
memory_kb: 19128
final_rank: 2

code_snippet: #line 1 "0355.test.cpp"
#include <algorithm>
#include <array>
#include <cassert>
#include <random>
#include <vector>

#line 4 "/Users/gyouzasushi/kyopro/library/datastructure/lazy_segment_tree.hpp"
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S),
          F (*composition)(F, F), F (*id)()>
struct LazySegmentTree {
public:
    LazySegmentTree() : LazySegmentTree(0) {
    }
    LazySegmentTree(int n) : LazySegmentTree(std::vector<S>(n, e())) {
    }
    LazySegmentTree(const std::vector<S>& v) : _n(int(v.size())) {
        log = ceil_pow2(_n);
        size = 1 << log;
        d = std::vector<S>(2 * size, e());
        lz = std::vector<F>(size, id());
        for (int i = 0; i < _n; i++) d[size + i] = v[i];
        for (int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }

    void set(int p, S x) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        d[p] = x;
        for (int i = 1; i <= log; i++) update(p >> i);
    }

    S get(int p) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        return d[p];
    }

    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        if (l == r) return e();

        l += size;
        r += size;

        for (int i = log; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push(r >> i);
        }

        S sml = e(), smr = e();
        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1;
            r >>= 1;
        }

        return op(sml, smr);
    }

    S all_prod() {
        return d[1];
    }

    void apply(int p, F f) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        d[p] = mapping(f, d[p]);
        for (int i = 1; i <= log; i++) update(p >> i);
    }
    void apply(int l, int r, F f) {
        assert(0 <= l && l <= r && r <= _n);
        if (l == r) return;

        l += size;
        r += size;

        for (int i = log; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push((r - 1) >> i);
        }

        {
            int l2 = l, r2 = r;
            while (l < r) {
                if (l & 1) all_apply(l++, f);
                if (r & 1) all_apply(--r, f);
                l >>= 1;
                r >>= 1;
            }
            l = l2;
            r = r2;
        }

        for (int i = 1; i <= log; i++) {
            if (((l >> i) << i) != l) update(l >> i);
            if (((r >> i) << i) != r) update((r - 1) >> i);
        }
    }

    template <bool (*g)(S)>
    int max_right(int l) {
        return max_right(l, [](S x) { return g(x); });
    }
    template <class G>
    int max_right(int l, G g) {
        assert(0 <= l && l <= _n);
        assert(g(e()));
        if (l == _n) return _n;
        l += size;
        for (int i = log; i >= 1; i--) push(l >> i);
        S sm = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!g(op(sm, d[l]))) {
                while (l < size) {
                    push(l);
                    l = (2 * l);
                    if (g(op(sm, d[l]))) {
                        sm = op(sm, d[l]);
                        l++;
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l]);
            l++;
        } while ((l & -l) != l);
        return _n;
    }

    template <bool (*g)(S)>
    int min_left(int r) {
        return min_left(r, [](S x) { return g(x); });
    }
    template <class G>
    int min_left(int r, G g) {
        assert(0 <= r && r <= _n);
        assert(g(e()));
        if (r == 0) return 0;
        r += size;
        for (int i = log; i >= 1; i--) push((r - 1) >> i);
        S sm = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!g(op(d[r], sm))) {
                while (r < size) {
                    push(r);
                    r = (2 * r + 1);
                    if (g(op(d[r], sm))) {
                        sm = op(d[r], sm);
                        r--;
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }

private:
    int _n, size, log;
    std::vector<S> d;
    std::vector<F> lz;
    int ceil_pow2(int n) {
        int x = 0;
        while ((1U << x) < (unsigned int)(n)) x++;
        return x;
    }
    void update(int k) {
        d[k] = op(d[2 * k], d[2 * k + 1]);
    }
    void all_apply(int k, F f) {
        d[k] = mapping(f, d[k]);
        if (k < size) lz[k] = composition(f, lz[k]);
    }
    void push(int k) {
        all_apply(2 * k, lz[k]);
        all_apply(2 * k + 1, lz[k]);
        lz[k] = id();
    }
};
#line 2 "/Users/gyouzasushi/kyopro/library/math/modint2305843009213693951.hpp"
#include <cstdint>
struct modint2305843009213693951 {
    using mint = modint2305843009213693951;

public:
    static constexpr uint64_t mod = 2305843009213693951;
    modint2305843009213693951() : _v(0) {
    }
    modint2305843009213693951(uint64_t v) : _v(fast_mod(v)) {
    }
    static constexpr uint64_t fast_mod(uint64_t v) {
        uint64_t u = v >> 61;
        uint64_t d = v & mod;
        uint64_t x = u + d;
        if (x > mod) x -= mod;
        return x;
    }
    uint64_t val() const {
        return _v;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= mod) _v -= mod;
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= mod) _v += mod;
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        static constexpr uint64_t mask31 = (uint64_t(1) << 31) - 1;
        static constexpr uint64_t mask30 = (uint64_t(1) << 30) - 1;
        uint64_t au = _v >> 31;
        uint64_t ad = _v & mask31;
        uint64_t bu = rhs._v >> 31;
        uint64_t bd = rhs._v & mask31;
        uint64_t m = ad * bu + au * bd;
        uint64_t mu = m >> 30;
        uint64_t md = m & mask30;
        _v = fast_mod((au * bu << 1) + mu + (md << 31) + ad * bd);
        return *this;
    }
    mint operator+() const {
        return *this;
    }
    mint operator-() const {
        return mint() - *this;
    }
    mint pow(uint64_t n) const {
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

private:
    uint64_t _v;
};
#line 2 "/Users/gyouzasushi/kyopro/library/math/pow_sum_table.hpp"
template <typename mint>
struct pow_mod_sums {
    pow_mod_sums() {
    }
    pow_mod_sums(mint base, int n) : base(base) {
        ensure(n);
    }
    // sum(pow[0..i])
    const mint& operator[](int i) const {
        ensure(i);
        return pow_sums[i];
    }
    void ensure(int n) const {
        int sz = pow_sums.size();
        if (sz > n) return;
        pow_sums.resize(n + 1);
        pows.resize(n + 1);
        for (int i = sz; i <= n; i++) {
            pows[i] = base * pows[i - 1];
            pow_sums[i] = pow_sums[i - 1] + pows[i - 1];
        }
    }

private:
    mutable std::vector<mint> pow_sums{0};
    mutable std::vector<mint> pows{1};
    mint base;
    static constexpr int mod = mint::mod;
};
#line 3 "/Users/gyouzasushi/kyopro/library/math/pow_table.hpp"
template <typename mint>
struct pow_mods {
    pow_mods() {
    }
    pow_mods(mint base, int n) : base(base) {
        ensure(n);
    }
    const mint& operator[](int i) const {
        ensure(i);
        return pows[i];
    }
    void ensure(int n) const {
        int sz = pows.size();
        if (sz > n) return;
        pows.resize(n + 1);
        for (int i = sz; i <= n; i++) pows[i] = base * pows[i - 1];
    }

private:
    mutable std::vector<mint> pows{1};
    mint base;
    static constexpr int mod = mint::mod;
};
#line 11 "0355.test.cpp"
template <int base_num = 1, typename mint = modint2305843009213693951>
struct RangeUpdateRangeHash {
public:
    RangeUpdateRangeHash() {
    }
    RangeUpdateRangeHash(const std::vector<int>& a) : n(a.size()) {
        std::vector<S> v(n);
        for (int i = 0; i < n; i++) {
            for (int base_id = 0; base_id < base_num; base_id++) {
                v[i].hash[base_id] = a[i];
            }
            v[i].size = 1;
            v[i].is_e = false;
        }
        segt = LazySegmentTree<S, op, e, F, mapping, composition, id>(v);
    }
    template <typename Iterable>
    static RangeUpdateRangeHash from(const Iterable& s) {
        std::vector<int> a;
        for (auto&& e : s) a.push_back(int(e));
        return RangeUpdateRangeHash(a);
    }

    template <typename T>
    void set(int p, T x) {
        segt.set(p, int(x));
    }
    std::array<mint, base_num> get(int p) {
        return segt.get(p).hash;
    }
    std::array<mint, base_num> prod(int l, int r) {
        return segt.prod(l, r).hash;
    }
    std::array<mint, base_num> all_prod() {
        return segt.all_prod();
    }
    template <typename T>
    void apply(int l, int r, T x) {
        segt.apply(l, r, {int(x), false});
    }
    int lcp(int l1, int r1, int l2, int r2) {
        int len = std::min(r1 - l1, r2 - l2);
        int ok = 0, ng = len + 1;
        while (ng - ok > 1) {
            int mid = (ok + ng) / 2;
            bool f = prod(l1, l1 + mid) == prod(l2, l2 + mid);
            (f ? ok : ng) = mid;
        }
        return ok;
    }
    int cmp(int l1, int r1, int l2, int r2) {
        int x = std::min({lcp(l1, r1, l2, r2), r1 - l1, r2 - l2});
        if (l1 + x == r1 && l2 + x != r2) return -1;
        if (l1 + x == r1 && l2 + x == r2) return 0;
        if (l1 + x != r1 && l2 + x == r2) return 1;
        return get(l1 + x)[0].val() < get(l2 + x)[0].val() ? -1 : 1;
    }

private:
    static inline std::array<mint, base_num> gen_bases() {
        static std::mt19937_64 rng(std::random_device{}());
        std::array<mint, base_num> bases;
        for (int i = 0; i < base_num; i++) {
            while (true) {
                uint64_t k = std::uniform_int_distribution<uint64_t>(
                    1, mint::mod - 1)(rng);
                if (std::gcd(k, mint::mod - 1) != 1) continue;
                uint64_t b = mint(r).pow(k).val();
                if (b <= A) continue;
                bases[i] = b;
                break;
            }
        }
        return bases;
    }
    static inline std::array<pow_mod_sums<mint>, base_num> init_pow_sums(
        const std::array<mint, base_num>& bases) {
        std::array<pow_mod_sums<mint>, base_num> pow_sums;
        for (int i = 0; i < base_num; i++) {
            pow_sums[i] = pow_mod_sums<mint>(bases[i], 0);
        }
        return pow_sums;
    }
    static inline std::array<pow_mods<mint>, base_num> init_pows(
        const std::array<mint, base_num>& bases) {
        std::array<pow_mods<mint>, base_num> pows;
        for (int i = 0; i < base_num; i++) {
            pows[i] = pow_mods<mint>(bases[i], 0);
        }
        return pows;
    }
    static inline std::array<mint, base_num> bases = gen_bases();
    static inline std::array<pow_mod_sums<mint>, base_num> pow_sums =
        init_pow_sums(bases);
    static inline std::array<pow_mods<mint>, base_num> pows = init_pows(bases);
    int n;
    static constexpr uint64_t r = 37;
    static constexpr uint64_t A = 2147483647;

    struct S {
        std::array<mint, base_num> hash;
        int size;
        bool is_e;
    };
    static S op(S a, S b) {
        if (a.is_e) return b;
        if (b.is_e) return a;
        for (int base_id = 0; base_id < base_num; base_id++) {
            b.hash[base_id] += a.hash[base_id] * pows[base_id][b.size];
        }
        b.size += a.size;
        return b;
    }
    static S e() {
        return {std::array<mint, base_num>(), 0, true};
    }
    struct F {
        int f;
        bool is_id;
    };
    static S mapping(F f, S x) {
        if (!f.is_id) {
            for (int base_id = 0; base_id < base_num; base_id++) {
                x.hash[base_id] = f.f * pow_sums[base_id][x.size];
            }
        }
        return x;
    }
    static F composition(F f, F g) {
        return (f.is_id ? g : f);
    }
    static F id() {
        return {0, true};
    }
    LazySegmentTree<S, op, e, F, mapping, composition, id> segt;
};

#include <iostream>
int main() {
    int n;
    std::string u;
    std::cin >> n >> u;
    auto rh = RangeUpdateRangeHash<>::from(u);
    int q;
    std::cin >> q;
    while (q--) {
        std::string t;
        std::cin >> t;
        if (t == "set") {
            int x, y;
            char z;
            std::cin >> x >> y >> z;
            rh.apply(--x, y, z);
        }
        if (t == "comp") {
            int a, b, c, d;
            std::cin >> a >> b >> c >> d;
            int sgn = rh.cmp(--a, b, --c, d);
            std::cout << (sgn == -1 ? 's' : sgn == 0 ? 'e' : 't') << '\n';
        }
    }
}
accuracy: 1
time_ms: 1910
memory_kb: 19036
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
const long long BASE = 1234567;
const long long INV = 616677444;
const long long MOD = 1000000007;
long long modpow(long long a, long long b){
  long long ans = 1;
  while (b > 0){
    if (b % 2 == 1){
      ans *= a;
      ans %= MOD;
    }
    a *= a;
    a %= MOD;
    b /= 2;
  }
  return ans;
}
long long modinv(long long a){
  return modpow(a, MOD - 2);
}
struct lazy_segment_tree{
  int N;
  vector<long long> sum, p, lazy;
  lazy_segment_tree(string S){
    int N2 = S.size();
    N = 1;
    while (N < N2){
      N *= 2;
    }
    sum = vector<long long>(N * 2 - 1, 0);
    p = vector<long long>(N * 2 - 1, BASE);
    lazy = vector<long long>(N * 2 - 1, -1);
    for (int i = 0; i < N2; i++){
      sum[N - 1 + i] = S[i];
    }
    for (int i = N - 2; i >= 0; i--){
      sum[i] = (sum[i * 2 + 1] + sum[i * 2 + 2] * p[i * 2 + 1]) % MOD;
      p[i] = p[i * 2 + 1] * p[i * 2 + 2] % MOD;
    }
  }
  void eval(int i){
    if (lazy[i] != -1){
      if (i < N - 1){
        lazy[i * 2 + 1] = lazy[i];
        lazy[i * 2 + 2] = lazy[i];
      }
      sum[i] = lazy[i] * (p[i] - 1) % MOD * INV % MOD;
      lazy[i] = -1;
    }
  }
  void range_update(int L, int R, int x, int i, int l, int r){
    eval(i);
    if (r <= L || R <= l){
      return;
    } else if (L <= l && r <= R){
      lazy[i] = x;
      eval(i);
    } else {
      int m = (l + r) / 2;
      range_update(L, R, x, i * 2 + 1, l, m);
      range_update(L, R, x, i * 2 + 2, m, r);
      sum[i] = (sum[i * 2 + 1] + sum[i * 2 + 2] * p[i * 2 + 1]) % MOD;
    }
  }
  void range_update(int L, int R, int x){
    range_update(L, R, x, 0, 0, N);
  }
  pair<long long, long long> range_sum(int L, int R, int i, int l, int r){
    eval(i);
    if (r <= L || R <= l){
      return make_pair(0, 1);
    } else if (L <= l && r <= R){
      return make_pair(sum[i], p[i]);
    } else {
      int m = (l + r) / 2;
      pair<long long, long long> P1 = range_sum(L, R, i * 2 + 1, l, m);
      pair<long long, long long> P2 = range_sum(L, R, i * 2 + 2, m, r);
      long long S = (P1.first + P2.first * P1.second) % MOD;
      long long P = P1.second * P2.second % MOD;
      return make_pair(S, P);
    }
  }
  pair<long long, long long> range_sum(int L, int R){
    return range_sum(L, R, 0, 0, N);
  }
};
int main(){
  int N;
  cin >> N;
  string U;
  cin >> U;
  lazy_segment_tree ST(U);
  int Q;
  cin >> Q;
  for (int i = 0; i < Q; i++){
    string t;
    cin >> t;
    if (t == "set"){
      int x, y;
      char z;
      cin >> x >> y >> z;
      x--;
      ST.range_update(x, y, z);
    }
    if (t == "comp"){
      int a, b, c, d;
      cin >> a >> b >> c >> d;
      a--;
      c--;
      int tv = 0, fv = min(b - a, d - c) + 1;
      while (fv - tv > 1){
        int mid = (tv + fv) / 2;
        long long hash1 = ST.range_sum(a, a + mid).first;
        long long hash2 = ST.range_sum(c, c + mid).first;
        if (hash1 == hash2){
          tv = mid;
        } else {
          fv = mid;
        }
      }
      if (tv == b - a && tv == d - c){
        cout << "e" << endl;
      } else if (tv == b - a){
        cout << "s" << endl;
      } else if (tv == d - c){
        cout << "t" << endl;
      } else {
        long long x = ST.range_sum(a + tv, a + tv + 1).first;
        long long y = ST.range_sum(c + tv, c + tv + 1).first;
        if (x < y){
          cout << "s" << endl;
        } else {
          cout << "t" << endl;
        }
      }
    }
  }
}
accuracy: 1
time_ms: 1030
memory_kb: 15980
final_rank: 6

code_snippet: #include<iostream>
#include<algorithm>

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif


#include <utility>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

struct barrett {
    unsigned int _m;
    unsigned long long im;

    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    unsigned int umod() const { return _m; }

    unsigned int mul(unsigned int a, unsigned int b) const {

        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b


        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder


#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>


#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

int ceil_pow2(int n) {
    int x = 0;
    while ((1U << x) < (unsigned int)(n)) x++;
    return x;
}

int bsf(unsigned int n) {
#ifdef _MSC_VER
    unsigned long index;
    _BitScanForward(&index, n);
    return index;
#else
    return __builtin_ctz(n);
#endif
}

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class S,
          S (*op)(S, S),
          S (*e)(),
          class F,
          S (*mapping)(F, S),
          F (*composition)(F, F),
          F (*id)()>
struct lazy_segtree {
  public:
    lazy_segtree() : lazy_segtree(0) {}
    lazy_segtree(int n) : lazy_segtree(std::vector<S>(n, e())) {}
    lazy_segtree(const std::vector<S>& v) : _n(int(v.size())) {
        log = internal::ceil_pow2(_n);
        size = 1 << log;
        d = std::vector<S>(2 * size, e());
        lz = std::vector<F>(size, id());
        for (int i = 0; i < _n; i++) d[size + i] = v[i];
        for (int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }

    void set(int p, S x) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        d[p] = x;
        for (int i = 1; i <= log; i++) update(p >> i);
    }

    S get(int p) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        return d[p];
    }

    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        if (l == r) return e();

        l += size;
        r += size;

        for (int i = log; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push(r >> i);
        }

        S sml = e(), smr = e();
        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1;
            r >>= 1;
        }

        return op(sml, smr);
    }

    S all_prod() { return d[1]; }

    void apply(int p, F f) {
        assert(0 <= p && p < _n);
        p += size;
        for (int i = log; i >= 1; i--) push(p >> i);
        d[p] = mapping(f, d[p]);
        for (int i = 1; i <= log; i++) update(p >> i);
    }
    void apply(int l, int r, F f) {
        assert(0 <= l && l <= r && r <= _n);
        if (l == r) return;

        l += size;
        r += size;

        for (int i = log; i >= 1; i--) {
            if (((l >> i) << i) != l) push(l >> i);
            if (((r >> i) << i) != r) push((r - 1) >> i);
        }

        {
            int l2 = l, r2 = r;
            while (l < r) {
                if (l & 1) all_apply(l++, f);
                if (r & 1) all_apply(--r, f);
                l >>= 1;
                r >>= 1;
            }
            l = l2;
            r = r2;
        }

        for (int i = 1; i <= log; i++) {
            if (((l >> i) << i) != l) update(l >> i);
            if (((r >> i) << i) != r) update((r - 1) >> i);
        }
    }

    template <bool (*g)(S)> int max_right(int l) {
        return max_right(l, [](S x) { return g(x); });
    }
    template <class G> int max_right(int l, G g) {
        assert(0 <= l && l <= _n);
        assert(g(e()));
        if (l == _n) return _n;
        l += size;
        for (int i = log; i >= 1; i--) push(l >> i);
        S sm = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!g(op(sm, d[l]))) {
                while (l < size) {
                    push(l);
                    l = (2 * l);
                    if (g(op(sm, d[l]))) {
                        sm = op(sm, d[l]);
                        l++;
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l]);
            l++;
        } while ((l & -l) != l);
        return _n;
    }

    template <bool (*g)(S)> int min_left(int r) {
        return min_left(r, [](S x) { return g(x); });
    }
    template <class G> int min_left(int r, G g) {
        assert(0 <= r && r <= _n);
        assert(g(e()));
        if (r == 0) return 0;
        r += size;
        for (int i = log; i >= 1; i--) push((r - 1) >> i);
        S sm = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!g(op(d[r], sm))) {
                while (r < size) {
                    push(r);
                    r = (2 * r + 1);
                    if (g(op(d[r], sm))) {
                        sm = op(d[r], sm);
                        r--;
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }

  private:
    int _n, size, log;
    std::vector<S> d;
    std::vector<F> lz;

    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
    void all_apply(int k, F f) {
        d[k] = mapping(f, d[k]);
        if (k < size) lz[k] = composition(f, lz[k]);
    }
    void push(int k) {
        all_apply(2 * k, lz[k]);
        all_apply(2 * k + 1, lz[k]);
        lz[k] = id();
    }
};

}  // namespace atcoder

using namespace std;
using namespace atcoder;
using mint=modint1000000007;
const mint Base=113;
int N,Q;
string U;
mint pB[2<<17],invpB[2<<17];
struct dat{
	mint V,B;
	dat():V(0),B(0){}
	dat(mint v,mint b):V(v),B(b){}
};
dat op(dat a,dat b){return dat(a.V+b.V,a.B+b.B);}
dat e(){return dat(0,0);}
dat mapping(int f,dat x){return f==-1?x:dat(x.B*f,x.B);}
int composition(int f,int g){return f==-1?g:f;}
int id(){return -1;}
main()
{
	cin>>N>>U>>Q;
	vector<dat>init(N);
	pB[0]=1;
	invpB[0]=1;
	mint invB=Base.inv();
	for(int i=0;i<N;i++)
	{
		pB[i+1]=pB[i]*Base;
		invpB[i+1]=invpB[i]*invB;
		init[i]=dat(pB[i]*U[i],pB[i]);
	}
	lazy_segtree<dat,op,e,int,mapping,composition,id>P(init);
	for(;Q--;)
	{
		string query;
		cin>>query;
		if(query=="set")
		{
			int x,y;char z;
			cin>>x>>y>>z;
			P.apply(x-1,y,(int)z);
		}
		else
		{
			int a,b,c,d;
			cin>>a>>b>>c>>d;
			a--,c--;
			int len=min(b-a,d-c);
			int L=0,R=len+1;
			while(R-L>1)
			{
				int mid=(L+R)/2;
				mint x=P.prod(a,a+mid).V,y=P.prod(c,c+mid).V;
				x*=invpB[a];
				y*=invpB[c];
				if(x!=y)R=mid;
				else L=mid;
			}
			if(L<len)
			{
				int x=(P.get(a+L).V*invpB[a+L]).val();
				int y=(P.get(c+L).V*invpB[c+L]).val();
				cout<<(x<y?"s":"t")<<endl;
			}
			else
			{
				cout<<(b-a<d-c?"s":b-a>d-c?"t":"e")<<endl;
			}
		}
	}
}
accuracy: 1
time_ms: 680
memory_kb: 11948
final_rank: 1

code_snippet: #include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <numeric>
#include <bitset>
#include <cmath>
#include <chrono>

static const int MOD1 = 1000000861, MOD2 = 1000000933;
using ll = long long;
using u32 = unsigned;
using u64 = unsigned long long;
using namespace std;

template<class T> constexpr T INF = ::numeric_limits<T>::max()/32*15+208;
class xor_shift {
    uint32_t x, y, z, w;
public:
    xor_shift() : x(static_cast<uint32_t>((chrono::system_clock::now().time_since_epoch().count())&((1LL << 32)-1))),
    y(1068246329), z(321908594), w(1234567890) {};

    uint32_t urand(){
        uint32_t t;
        t = x ^ (x << 11);
        x = y; y = z; z = w;
        w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
        return w;
    };

    int rand(int n){
        if(n < 0) return -rand(-n);
        uint32_t t = numeric_limits<uint32_t>::max()/(n+1)*(n+1);
        uint32_t e = urand();
        while(e >= t) e = urand();
        return static_cast<int>(e%(n+1));
    }

    int rand(int a, int b){
        if(a > b) swap(a, b);
        return a+rand(b-a);
    }
};


xor_shift rd;

template<int M>
struct rolling_hash {
    static ll &B() {
        static ll B_ = rd.rand(2, M-1);
        return B_;
    }
    static vector<ll> &p() {
        static vector<ll> p_{1, B()};
        return p_;
    }
    vector<ll> hash;
    explicit rolling_hash(const string &s) {
        if(p().size() <= s.size()){
            int l = p().size();
            p().resize(s.size()+1);
            for (int i = l; i < p().size(); ++i) {
                p()[i] = (p()[i-1]*p()[1])%M;
            }
        }
        hash.resize(s.size()+1, 0);
        for (int i = 0; i < s.size(); ++i) {
            hash[i+1] = (hash[i]*B() + s[i]) % M;
        }
    };

    ll get(int l, int r){
        ll res = hash[r]+M-hash[l]*p()[r-l]%M;
        return res >= M ? res-M : res;
    }
};


vector<rolling_hash<MOD1>> hashs1;
vector<rolling_hash<MOD2>> hashs2;

template <class M>
struct LazySegmentTree{
    using T = typename M::T;
    using L = typename M::L;
    int sz, height{};
    vector<T> seg; vector<L> lazy;
    explicit LazySegmentTree(int n) {
        sz = 1; while(sz < n) sz <<= 1, height++;
        seg.assign(2*sz, M::e());
        lazy.assign(2*sz, M::l());
    }

    void set(int k, const T &x){
        seg[k + sz] = x;
    }

    void build(){
        for (int i = sz-1; i > 0; --i) seg[i] = M::f(seg[i<<1], seg[(i<<1)|1]);
    }

    T reflect(int k){ return lazy[k] == M::l() ? seg[k] : M::g(seg[k], lazy[k]); }

    void eval(int k){
        if(lazy[k] == M::l()) return;
        lazy[(k<<1)|0] = M::h(lazy[(k<<1)|0], lazy[k]);
        lazy[(k<<1)|1] = M::h(lazy[(k<<1)|1], lazy[k]);
        seg[k] = reflect(k);
        lazy[k] = M::l();
    }
    void thrust(int k){ for (int i = height; i; --i) eval(k>>i); }
    void recalc(int k) { while(k >>= 1) seg[k] = M::f(reflect((k<<1)|0), reflect((k<<1)|1));}
    void update(int a, int b, const L &x){
        thrust(a += sz); thrust(b += sz-1);
        for (int l = a, r = b+1;l < r; l >>=1, r >>= 1) {
            if(l&1) lazy[l] = M::h(lazy[l], x), l++;
            if(r&1) --r, lazy[r] = M::h(lazy[r], x);
        }
        recalc(a);
        recalc(b);
    }

    T query(int a, int b){ // [l, r)
        thrust(a += sz);
        thrust(b += sz-1);
        T ll = M::e(), rr = M::e();
        for(int l = a, r = b+1; l < r; l >>=1, r>>=1) {
            if (l & 1) ll = M::f(ll, reflect(l++));
            if (r & 1) rr = M::f(reflect(--r), rr);
        }
        return M::f(ll, rr);
    }
};

struct Monoid{
    using T = array<ll, 3>;
    using L = char;
    static T f(T a, T b) {
        return {(a[0]*rolling_hash<MOD1>::p()[b[2]]+b[0])%MOD1, (a[1]*rolling_hash<MOD2>::p()[b[2]]+b[1])%MOD2, a[2]+b[2]};
    }
    static T g(T a, L b) {
        if(b == l()) return a;
        else {
            return {hashs1[b-'a'].get(0, a[2]), hashs2[b-'a'].get(0, a[2]), a[2]};
        }
    }
    static L h(L a, L b) {
        if(b == l()) return a; else return b;
    }
    static T e() { return {0, 0, 0}; }
    static L l() { return 0; }
};

int main() {
    int n;
    string s;
    cin >> n >> s;
    for (int i = 'a'; i <= 'z'; ++i) {
        hashs1.emplace_back(string(n, i));
        hashs2.emplace_back(string(n, i));
    }
    LazySegmentTree<Monoid> seg(n);
    for (int i = 0; i < n; ++i) {
        seg.set(i, {s[i], s[i], 1});
    }
    seg.build();
    int q;
    cin >> q;
    for (int i = 0; i < q; ++i) {
        string qq;
        cin >> qq;
        if(qq == "comp"){
            int a, b, c, d;
            cin >> a >> b >> c >> d;
            a--; c--;
            int ok = 0, ng = min(b-a+1, d-c+1);
            while(ng-ok > 1){
                int mid = (ok+ng)/2;
                if(seg.query(a, a+mid) == seg.query(c, c+mid)) ok = mid;
                else ng = mid;
            }
            if(a+ok == b && c+ok == d){
                puts("e");
            }else if(a+ok == b){
                puts("s");
            }else if(c+ok == d){
                puts("t");
            }else if(seg.query(a+ok, a+ng) < seg.query(c+ok, c+ng)){
                puts("s");
            }else {
                puts("t");
            }
        }else {
            int a, b; char c;
            cin >> a >> b >> c;
            a--;
            seg.update(a, b, c);
        }
    }
    return 0;
}
accuracy: 1
time_ms: 1090
memory_kb: 100544
final_rank: 18

code_snippet: #line 1 "test/aoj_0355.test.cpp"
#define PROBLEM "http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0355"

#include <cstdint>
#include <cstdio>
#include <algorithm>
#include <utility>
#include <vector>

#line 1 "ModularArithmetic/modint.cpp"



/**
 * @brief 合同算術用クラス
 * @author えびちゃん
 */

#line 10 "ModularArithmetic/modint.cpp"
#include <limits>
#include <type_traits>
#line 13 "ModularArithmetic/modint.cpp"

template <intmax_t Modulo>
class modint {
public:
  using value_type = typename std::conditional<
  (0 < Modulo && Modulo < std::numeric_limits<int>::max() / 2), int, intmax_t
 >::type;

private:
  static constexpr value_type S_cmod = Modulo;  // compile-time
  static value_type S_rmod;  // runtime
  value_type M_value = 0;

  static constexpr value_type S_inv(value_type n, value_type m) {
    value_type x = 0;
    value_type y = 1;
    value_type a = n;
    value_type b = m;
    for (value_type u = y, v = x; a;) {
      value_type q = b / a;
      std::swap(x -= q*u, u);
      std::swap(y -= q*v, v);
      std::swap(b -= q*a, a);
    }
    if ((x %= m) < 0) x += m;
    return x;
  }

  static value_type S_normalize(intmax_t n, value_type m) {
    if (n >= m) {
      n %= m;
    } else if (n < 0) {
      if ((n %= m) < 0) n += m;
    }
    return n;
  }

public:
  modint() = default;
  modint(intmax_t n): M_value(S_normalize(n, get_modulo())) {}

  modint& operator =(intmax_t n) {
    M_value = S_normalize(n, get_modulo());
    return *this;
  }

  modint& operator +=(modint const& that) {
    if ((M_value += that.M_value) >= get_modulo()) M_value -= get_modulo();
    return *this;
  }
  modint& operator -=(modint const& that) {
    if ((M_value -= that.M_value) < 0) M_value += get_modulo();
    return *this;
  }
  modint& operator *=(modint const& that) {
    intmax_t tmp = M_value;
    tmp *= that.M_value;
    M_value = tmp % get_modulo();
    return *this;
  }
  modint& operator /=(modint const& that) {
    intmax_t tmp = M_value;
    tmp *= S_inv(that.M_value, get_modulo());
    M_value = tmp % get_modulo();
    return *this;
  }

  modint& operator ++() {
    if (++M_value == get_modulo()) M_value = 0;
    return *this;
  }
  modint& operator --() {
    if (M_value-- == 0) M_value = get_modulo()-1;
    return *this;
  }

  modint operator ++(int) { modint tmp(*this); ++*this; return tmp; }
  modint operator --(int) { modint tmp(*this); --*this; return tmp; }

  friend modint operator +(modint lhs, modint const& rhs) { return lhs += rhs; }
  friend modint operator -(modint lhs, modint const& rhs) { return lhs -= rhs; }
  friend modint operator *(modint lhs, modint const& rhs) { return lhs *= rhs; }
  friend modint operator /(modint lhs, modint const& rhs) { return lhs /= rhs; }

  modint operator +() const { return *this; }
  modint operator -() const {
    if (M_value == 0) return *this;
    return modint(get_modulo() - M_value);
  }

  friend bool operator ==(modint const& lhs, modint const& rhs) {
    return lhs.M_value == rhs.M_value;
  }
  friend bool operator !=(modint const& lhs, modint const& rhs) {
    return !(lhs == rhs);
  }

  value_type get() const { return M_value; }
  static value_type get_modulo() { return ((S_cmod > 0)? S_cmod: S_rmod); }

  template <int M = Modulo, typename Tp = typename std::enable_if<(M <= 0)>::type>
  static Tp set_modulo(value_type m) { S_rmod = m; }
};

template <intmax_t N>
constexpr typename modint<N>::value_type modint<N>::S_cmod;
template <intmax_t N>
typename modint<N>::value_type modint<N>::S_rmod;


#line 1 "utility/monoid/rolling_hash.cpp"



/**
 * @brief ロリハの演算のモノイド
 * @author えびちゃん
 */

#line 10 "utility/monoid/rolling_hash.cpp"

template <typename ModInt>
class rolling_hash_monoid {
public:
  using value_type = ModInt;

private:
  value_type M_x = 0, M_p = 1;

public:
  rolling_hash_monoid() = default;  // identity

  rolling_hash_monoid(value_type const& x, value_type const& p): M_x(x), M_p(p) {};

  rolling_hash_monoid& operator +=(rolling_hash_monoid const& that) {
    M_x = M_x * that.M_p + that.M_x;
    M_p *= that.M_p;
    return *this;
  }
  friend bool operator ==(rolling_hash_monoid const& lhs, rolling_hash_monoid const& rhs) {
    return lhs.M_x == rhs.M_x && lhs.M_p == rhs.M_p;
  }

  friend rolling_hash_monoid operator +(rolling_hash_monoid lhs, rolling_hash_monoid const& rhs) {
    return lhs += rhs;
  }
  friend bool operator !=(rolling_hash_monoid const& lhs, rolling_hash_monoid const& rhs) {
    return !(lhs == rhs);
  }

  value_type const& get() const { return M_x; }
  value_type const& coefficient() const { return M_p; }
};


#line 1 "utility/monoid/set.cpp"



/**
 * @brief モノイドクラス
 * @author えびちゃん
 */

template <typename Tp>
class set_monoid {
public:
  using value_type = Tp;

private:
  bool M_empty = true;
  value_type M_x;

public:
  set_monoid() = default;  // identity

  set_monoid(value_type const& x): M_empty(false), M_x(x) {}

  set_monoid& operator +=(set_monoid const& that) {
    M_empty = that.M_empty;
    if (!that.M_empty) M_x = that.M_x;
    return *this;
  }
  friend bool operator ==(set_monoid const& lhs, set_monoid const& rhs) {
    if (lhs.M_empty && rhs.M_empty) return true;
    if (lhs.M_empty != rhs.M_empty) return false;
    return lhs.M_x == rhs.M_x;
  }

  friend set_monoid operator +(set_monoid lhs, set_monoid const& rhs) { return lhs += rhs; }
  friend bool operator !=(set_monoid const& lhs, set_monoid const& rhs) {
    return !(lhs == rhs);
  }

  bool empty() const noexcept { return M_empty; }
  value_type const& get() const { return M_x; }
};


#line 1 "DataStructure/segment_tree.cpp"



/**
 * @brief 区間作用・区間和セグメント木
 * @author えびちゃん
 */

#include <cstddef>
#line 11 "DataStructure/segment_tree.cpp"

#line 1 "integer/bit.cpp"



/** 
 * @brief ビット演算
 * @author えびちゃん
 */

// XXX integral promotion 関連の注意をあまりしていません

#include <climits>
#include <type_traits>

template <typename Tp>
constexpr auto countl_zero(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, int>::type
{
  using value_type = typename std::make_unsigned<Tp>::type;
  int bits = (sizeof(value_type) * CHAR_BIT);
  if (n == 0) return bits;
  int res = 0;
  for (int i = bits / 2; i > 0; i /= 2) {
    value_type mask = ((static_cast<value_type>(1) << i) - 1) << i;
    if (n & mask) n >>= i;
    else res += i;
  }
  return res;
}
template <typename Tp>
constexpr auto countl_one(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, int>::type
{
  using value_type = typename std::make_unsigned<Tp>::type;
  return countl_zero(static_cast<value_type>(~n));
}

template <typename Tp>
constexpr auto countr_zero(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, int>::type
{
  using value_type = typename std::make_unsigned<Tp>::type;
  int bits = (sizeof(value_type) * CHAR_BIT);
  if (n == 0) return bits;
  int res = 0;
  for (int i = bits / 2; i > 0; i /= 2) {
    value_type mask = ((static_cast<value_type>(1) << i) - 1);
    if (!(n & mask)) res += i, n >>= i;
  }
  return res;
}
template <typename Tp>
constexpr auto countr_one(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, int>::type
{
  using value_type = typename std::make_unsigned<Tp>::type;
  return countr_zero(static_cast<value_type>(~n));
}

constexpr unsigned long long half_mask[] = {
  0x5555555555555555uLL, 0x3333333333333333uLL, 0x0F0F0F0F0F0F0F0FuLL,
  0x00FF00FF00FF00FFuLL, 0x0000FFFF0000FFFFuLL, 0x00000000FFFFFFFFuLL
};

template <typename Tp>
constexpr auto popcount(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, int>::type
{
  int bits = static_cast<int>((sizeof n) * CHAR_BIT);
  for (int i = 0, j = 1; j < bits; ++i, j *= 2) {
    if (j <= 8) n = (n & half_mask[i]) + ((n >> j) & half_mask[i]);
    else n += n >> j;
  }
  return n & 0xFF;
}

template <typename Tp>
constexpr auto parity(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, int>::type
{ return popcount(n) & 1; }


template <typename Tp>
int clz(Tp n) { return countl_zero(static_cast<typename std::make_unsigned<Tp>::type>(n)); }
template <typename Tp>
int ctz(Tp n) { return countr_zero(static_cast<typename std::make_unsigned<Tp>::type>(n)); }

template <typename Tp>
int ilog2(Tp n) {
  return (CHAR_BIT * sizeof(Tp) - 1) - clz(static_cast<typename std::make_unsigned<Tp>::type>(n));
}
template <typename Tp>
bool is_pow2(Tp n) { return (n > 0) && ((n & (n-1)) == 0); }
template <typename Tp>
Tp floor2(Tp n) { return is_pow2(n)? n: static_cast<Tp>(1) << ilog2(n); }
template <typename Tp>
Tp ceil2(Tp n) { return is_pow2(n)? n: static_cast<Tp>(2) << ilog2(n); }

template <typename Tp>
constexpr auto reverse(Tp n)
  -> typename std::enable_if<std::is_unsigned<Tp>::value, Tp>::type
{
  int bits = static_cast<int>((sizeof n) * CHAR_BIT);
  for (int i = 0, j = 1; j < bits; ++i, j *= 2) {
    n = ((n & half_mask[i]) << j) | ((n >> j) & half_mask[i]);
  }
  return n;
}


#line 13 "DataStructure/segment_tree.cpp"

template <typename Operation>
class segment_tree {
public:
  using size_type = size_t;
  using operation = Operation;
  using operand_type = typename operation::operand_type;
  using action_type = typename operation::action_type;
  using value_type = operand_type;

private:
  size_type M_n;
  std::vector<operand_type> M_c;
  std::vector<action_type> M_d;  // deferred

  void M_build(size_type i) {
    while (i > 1) {
      i >>= 1;
      M_c[i] = (M_c[i<<1|0] + M_c[i<<1|1]);
      operation::act(M_c[i], M_d[i]);
    }
  }

  void M_resolve(size_type i) {
    size_type h = ilog2(M_n) + 2;  // ilog2p1(M_n*2)
    for (size_type s = h; s > 0; --s) {
      size_type p = i >> s;
      action_type id{};
      if (M_d[p] != id) {
        M_apply(p<<1|0, M_d[p]);
        M_apply(p<<1|1, M_d[p]);
        M_d[p] = id;
      }
    }
  }

  void M_apply(size_type i, action_type const& x) {
    operation::act(M_c[i], x);
    if (i < M_n) M_d[i] += x;
  }

public:
  segment_tree() = default;
  explicit segment_tree(size_type n):
    M_n(n), M_c(n+n, operand_type{}), M_d(n, action_type{}) {}

  segment_tree(size_type n, operand_type const& x):
    M_n(n), M_c(n+n, x), M_d(n, action_type{})
  {
    for (size_type i = n; i--;) M_c[i] = M_c[i<<1|0] + M_c[i<<1|1];
  }

  template <typename InputIt>
  segment_tree(InputIt first, InputIt last): M_c(first, last) {
    M_n = M_c.size();
    M_d.assign(M_n, action_type{});
    M_c.insert(M_c.begin(), M_n, operand_type{});
    for (size_type i = M_n; i--;) M_c[i] = M_c[i<<1|0] + M_c[i<<1|1];
  }

  void assign(size_type n) {
    M_n = n;
    M_c(n+n, operand_type{});
    M_d(n, action_type{});
  }

  void assign(size_type n, operand_type const& x) {
    M_n = n;
    M_c(n+n, x);
    M_d(n, action_type{});
    for (size_type i = n; i--;) M_c[i] = M_c[i<<1|0] + M_c[i<<1|1];
  }

  template <typename InputIt>
  void assign(InputIt first, InputIt last) {
    M_c.assign(first, last);
    M_n = M_c.size();
    M_d.assign(M_n, action_type{});
    M_c.insert(M_c.begin(), M_n, operand_type{});
    for (size_type i = M_n; i--;) M_c[i] = M_c[i<<1|0] + M_c[i<<1|1];
  }

  void act(size_type l, size_type r, action_type const& x) {
    if (l == r) return;
    l += M_n;
    r += M_n;
    size_type l0 = l;
    size_type r0 = r;
    M_resolve(l0);
    M_resolve(r0-1);
    while (l < r) {
      if (l & 1) M_apply(l++, x);
      if (r & 1) M_apply(--r, x);
      l >>= 1;
      r >>= 1;
    }
    M_build(l0);
    M_build(r0-1);
  }

  operand_type fold(size_type l, size_type r) {
    operand_type resl{}, resr{};
    if (l == r) return resl;

    l += M_n;
    r += M_n;
    M_resolve(l);
    M_resolve(r-1);
    while (l < r) {
      if (l & 1) resl += M_c[l++];
      if (r & 1) resr = M_c[--r] + std::move(resr);
      l >>= 1;
      r >>= 1;
    }
    return resl += resr;
  }

  operand_type operator [](size_type i) {
    i += M_n;
    M_resolve(i);
    return M_c[i];
  }
};


#line 13 "test/aoj_0355.test.cpp"

constexpr intmax_t mod = 998244353;
using mi = modint<mod>;

template <typename ModInt>
struct action_set_to_rolling_hash {
  using operand_type = rolling_hash_monoid<ModInt>;
  using action_type = set_monoid<std::pair<ModInt, ModInt>>;

  static void act(operand_type& op, action_type const& a) {
    if (a.empty()) return;
    operand_type tmp(a.get().first, a.get().second);
    if (op.coefficient() == 1) {
      op = tmp;
      return;
    }
    while (tmp.coefficient() != op.coefficient()) tmp += tmp;
    op = tmp;
  }
};

int main() {
  size_t n;
  char buf[262144];
  int q;
  scanf("%zu %s %d", &n, buf, &q);
  std::string s = buf;

  intmax_t b1 = 2352983, b2 = 4986917;
  segment_tree<action_set_to_rolling_hash<mi>> s1(n), s2(n);
  auto f = [](auto x, auto y) {
    return set_monoid<std::pair<mi, mi>>({x, y});
  };
  for (size_t i = 0; i < n; ++i) {
    s1.act(i, i+1, f(s[i], b1));
    s2.act(i, i+1, f(s[i], b2));
  }

  for (int i = 0; i < q; ++i) {
    scanf("%s", buf);
    std::string type = buf;
    if (type == "set") {
      size_t x, y;
      char z;
      scanf("%zu %zu %c", &x, &y, &z);
      --x, --y;
      s1.act(x, y+1, f(z, b1));
      s2.act(x, y+1, f(z, b2));
    } else if (type == "comp") {
      size_t a, b, c, d;
      scanf("%zu %zu %zu %zu", &a, &b, &c, &d);
      --a, --b, --c, --d;
      size_t sl = b-a+1, tl = d-c+1;
      auto eq = [&](size_t l) {
        return s1.fold(a, a+l) == s1.fold(c, c+l) && s2.fold(a, a+l) == s2.fold(c, c+l);
      };
      if (eq(std::min(sl, tl))) {
        printf("%c\n", ((sl < tl)? 's': (sl > tl)? 't': 'e'));
        continue;
      }
      size_t lb = 0, ub = std::min(sl, tl);
      while (ub-lb > 1) {
        size_t mid = (lb+ub) >> 1;
        (eq(mid)? lb: ub) = mid;
      }
      int sc = s1[a+lb].get().get(), tc = s1[c+lb].get().get();
      printf("%c\n", ((sc < tc)? 's': 't'));
    }
  }
}
accuracy: 1
time_ms: 840
memory_kb: 13864
final_rank: 4

code_snippet: #ifndef LOCAL
#pragma GCC optimize ("O3")
#pragma GCC optimize ("unroll-loops")
#pragma GCC target ("avx")
#endif

#include <vector>
#include <set>
#include <map>
#include <tuple>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <algorithm>
#include <utility>
#include <numeric>
#include <iostream>
#include <cmath>
#include <cassert>
#include <cstring>

using namespace std;

using ll = long long;
using ull = unsigned long long;
using VI = vector<int>;
using VVI = vector<vector<int>>;
using VLL = vector<ll>;
using VVLL = vector<vector<ll>>;
using VB = vector<bool>;
using PII = pair<int, int>;
using PLL = pair<ll, ll>;
template <typename T>
using minheap = priority_queue<T, vector<T>, greater<T>>;
const int INF = 1000000007;
const ll INF_LL = 1'000'000'000'000'000'007;

#define __overload3(_1, _2, _3, name,...) name
#define rep(...) __overload3(__VA_ARGS__, repFromUntil, repUntil, repeat)(__VA_ARGS__)
#define repeat(times) repFromUntil(_name##__LINE__, 0, times)
#define repUntil(name, times) repFromUntil(name, 0, times)
#define repFromUntil(name, from, until) for (int name = from, name##__until = (until); name < name##__until; name++)
#define repr(...) __overload3(__VA_ARGS__, reprFromUntil, reprUntil, repeat)(__VA_ARGS__)
#define reprUntil(name, times) reprFromUntil(name, 0, times)
#define reprFromUntil(name, from, until) for (int name = (until) - 1, name##__from = (from); name >= name##__from; name--)

#define EXIT(out) do { OUT(out); exit(0); } while (0)

#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)

#ifdef LOCAL
#define debug(v) do {debugos << "L" << __LINE__ << " " << #v << " > ";debugos<<(v)<<newl;} while (0)
#define debugv(v) do {debugos << "L" << __LINE__ << " " << #v << " > ";for(auto e:(v)){debugos<<e<<" ";}debugos<<newl;} while (0)
#define debuga(m,w) do {debugos << "L" << __LINE__ << " " << #m << " > ";for(int x=0;x<(w);x++){debugos<<(m)[x]<<" ";}debugos<<newl;} while (0)
#define debugaa(m,h,w) do {debugos << "L" << __LINE__ << " " << #m << " > \n";for(int y=0;y<(h);y++){for(int x=0;x<(w);x++){debugos<<(m)[y][x]<<" ";}debugos<<newl;}} while (0)
#else
#define debug(v) do {v;} while (0)
#define debugv(v) do; while (0)
#define debuga(m,w) do; while (0)
#define debugaa(m,h,w) do; while (0)
#endif

#define newl "\n"

template <typename T, typename U> bool chmin(T& var, U x) {
  if (var > x) {
    var = x;
    return true;
  } else return false;
}

template <typename T, typename U> bool chmax(T& var, U x) {
  if (var < x) {
    var = x;
    return true;
  } else return false;
}

template <typename T> int sgn(T val) { return (T(0) < val) - (val < T(0)); }

ll power(ll e, int t, ll mod = INF_LL) {
  ll res = 1;
  while (t) {
    if (t&1) res = (res * e) % mod;
    t >>= 1;
    e = (e * e) % mod;
  }
  return res;
}

template <typename T> T divceil(T m, T d) {
  assert(m >= 0 and d > 0);
  return (m+d-1)/d;
}

template<typename T>
vector<T> make_v(size_t a, T b) { return vector<T>(a, b); }

template<typename... Ts>
auto make_v(size_t a, Ts... ts) {
  return vector<decltype(make_v(ts...))>(a, make_v(ts...));
}

string operator*(const string& s, int times) {
  string res = "";
  rep(times) res += s;
  return res;
}

struct Edge {
  int to; ll cost;
  Edge(int _to): to(_to), cost(1) {}
  Edge(int _to, ll _cost): to(_to), cost(_cost) {}
  operator int() { return to; }
};

using Graph = vector<vector<Edge>>;

class MyScanner {
public:
  int offset = 0;
  char nc(){
#ifdef LOCAL
    return getchar();
#else
    return getchar_unlocked();
#endif
  }
  template <typename T> void input_integer(T& var) {
    var = 0; T sign = 1;
    int cc = nc();
    for (; cc<'0' || '9'<cc; cc = nc())
      if (cc == '-') sign = -1;
    for (; '0' <= cc && cc <= '9'; cc = nc())
      var = (var << 3) + (var << 1) + cc - '0';
    var = var * sign; var += offset;
  }
  int c() { char c; while (c = nc(), c == ' ' or c == '\n'); return c; }
  MyScanner& operator>>(char& var) { var = c(); return *this; }
  MyScanner& operator>>(int& var) { input_integer<int>(var); return *this; }
  MyScanner& operator>>(ll& var) { input_integer<ll>(var); return *this; }
  MyScanner& operator>>(string& var) {
    var = ""; int cc = nc();
    for (; !isvisiblechar(cc); cc = nc());
    for (; isvisiblechar(cc); cc = nc())
      var.push_back(cc);
    return *this;
  }
  template <typename T>
  operator T() { T x; *this >> x; return x; }
  template <typename T>
  void operator()(T &t) { *this >> t; }
  template <typename T, typename... Ts>
  void operator()(T &t, Ts &...ts) { *this >> t; this->operator()(ts...); }
  template <typename Iter>
  void iter(Iter first, Iter last) { while (first != last) *this >> *first, first++; }
  VI vi(int n) { VI res(n); iter(all(res)); return res; }
  VVI vvi(int n, int m) { VVI res(n); rep(i, n) res[i] = vi(m); return res; }
  VLL vll(int n) { VLL res(n); iter(all(res)); return res; }
  VVLL vvll(int n, int m) { VVLL res(n); rep(i, n) res[i] = vll(m); return res; }
  template <typename T>
  vector<T> v(int n) { vector<T> res(n); iter(all(res)); return res; }
private:
  int isvisiblechar(int c) { return 0x21 <= c && c <= 0x7E; }
} IN, IN1{-1};

class MyPrinter {
public:
  int offset = 0;
  template <typename T>
  void output_integer(T var) {
    var += offset;
    if (var == 0) { putchar('0'); return; }
    if (var < 0) putchar('-'), var = -var;
    char stack[32]; int stack_p = 0;
    while (var) stack[stack_p++] = '0' + (var % 10), var /= 10;
    while (stack_p) putchar(stack[--stack_p]);
  }
  MyPrinter& operator<<(char c) { putchar(c); return *this; }
  MyPrinter& operator<<(double x) { printf("%.10f", x); return *this; }
  template <typename T> MyPrinter& operator<<(T var) { output_integer<T>(var); return *this; }
  MyPrinter& operator<<(char* str_p) { while (*str_p) putchar(*(str_p++)); return *this; }
  MyPrinter& operator<<(const char* str_p) { while (*str_p) putchar(*(str_p++)); return *this; }
  MyPrinter& operator<<(const string& str) {
    const char* p = str.c_str();
    const char* l = p + str.size();
    while (p < l) putchar(*p++);
    return *this;
  }
  template <typename T>
  void operator()(T x) { *this << x << newl; }
  template <typename T, typename... Ts>
  void operator()(T x, Ts ...xs) { *this << x << " "; this->operator()(xs...); }
  template <typename Iter>
  void iter(Iter s, Iter t) {
    if (s == t) *this << "\n";
    else for (; s != t; s++) *this << *s << " \n"[next(s, 1) == t];
  }
  template <typename Range>
  void range(const Range& r) { iter(begin(r), end(r)); }
} OUT, OUT1{1};

class DebugPrint {
public:
  template <typename T>
  DebugPrint& operator <<(const T& v) {
#ifdef LOCAL
    cerr << v;
#endif
    return *this;
  }
} debugos;

template <typename OutStream, typename T, typename U>
OutStream& operator<<(OutStream& out, const pair<T, U>& var) {
  return out << var.first << " " << var.second;
}
template <typename OutStream, typename Tuple, size_t I, size_t N,
          enable_if_t<I == N>* = nullptr>
OutStream& tuple_impl(OutStream& out, const Tuple& var) {
  return out;
}
template <typename OutStream, typename Tuple, size_t I, size_t N,
          enable_if_t<I != N>* = nullptr>
OutStream& tuple_impl(OutStream& out, const Tuple& var) {
  out << get<I>(var) << " ";
  return tuple_impl<OutStream, Tuple, I+1, N>(out, var);
}
template <typename OutStream, typename... Ts>
OutStream& operator<<(OutStream& out, const tuple<Ts...>& var) {
  return tuple_impl<OutStream, tuple<Ts...>, 0, sizeof...(Ts)>(out, var);
}
template <typename InStream, typename T, typename U>
InStream& operator>>(InStream& in, pair<T, U>& var) {
  return in >> var.first >> var.second;
}
template <typename InStream, typename Tuple, size_t I, size_t N,
          enable_if_t<I == N>* = nullptr>
InStream& tuple_impl(InStream& in, Tuple& var) {
  return in;
}
template <typename InStream, typename Tuple, size_t I, size_t N,
          enable_if_t<I != N>* = nullptr>
InStream& tuple_impl(InStream& in, Tuple& var) {
  in >> get<I>(var);
  return tuple_impl<InStream, Tuple, I+1, N>(in, var);
}
template <typename InStream, typename... Ts>
InStream& operator>>(InStream& in, tuple<Ts...>& var) {
  return tuple_impl<InStream, tuple<Ts...>, 0, sizeof...(Ts)>(in, var);
}


#pragma GCC diagnostic ignored "-Wshadow"
template <typename T, typename U, typename Merge, typename EMerge, typename Upd>
struct LazySegmentTree {
 private:
  const size_t n, h;
  const T unit;
  const U eunit;
  const Merge merge;
  const EMerge emerge;
  const Upd upd;
  vector<T> data;
  vector<U> lazy;

 public:
  LazySegmentTree(size_t n = 0, T unit = T(), U eunit = U(),
                  Merge merge = Merge(), EMerge emerge = EMerge(),
                  Upd upd = Upd())
      : n(n), h(32 - __builtin_clz(n)), unit(unit), eunit(eunit), merge(merge),
        emerge(emerge), upd(upd), data(n << 1, unit), lazy(n, eunit) {
    build(0, n);
  }

  template <typename Iter,
            enable_if_t<is_same<typename Iter::value_type, T>::value>* = nullptr>
  LazySegmentTree(Iter first, Iter last, size_t n, T unit = T(), U eunit = U(),
                  Merge merge = Merge(), EMerge emerge = EMerge(),
                  Upd upd = Upd())
      : n(n), h(32 - __builtin_clz(n)), unit(unit), eunit(eunit), merge(merge),
        emerge(emerge), upd(upd), data(n << 1, unit), lazy(n, eunit) {
    assign(first, last);
  }

  template <typename Iter,
            enable_if_t<!is_same<typename Iter::value_type, T>::value>* = nullptr>
  LazySegmentTree(Iter first, Iter last, size_t n, T unit = T(),
                                 U eunit = U(), Merge merge = Merge(),
                                 EMerge emerge = EMerge(), Upd upd = Upd())
      : n(n), h(32 - __builtin_clz(n)), unit(unit), eunit(eunit), merge(merge),
        emerge(emerge), upd(upd), data(n << 1, unit), lazy(n, eunit) {
    assign(first, last);
  }

  template <typename Iter>
  LazySegmentTree(Iter first, Iter last, T unit = T(), U eunit = U(),
                  Merge merge = Merge(), EMerge emerge = EMerge(),
                  Upd upd = Upd())
      : LazySegmentTree(first, last, distance(first, last), unit, eunit, merge,
                        emerge, upd) {}

 private:
  void apply(int p, U e, int sz) {
    if (e == eunit) return;
    data[p] = upd(data[p], e, sz);
    if (p < n) {
      if (lazy[p] == eunit) lazy[p] = e;
      else lazy[p] = emerge(lazy[p], e);
    }
  }

  void pushdown(int p, int sz) {
    if (p >= n or lazy[p] == eunit) return;
    apply(p << 1, lazy[p], sz >> 1);
    apply(p << 1 | 1, lazy[p], sz >> 1);
    lazy[p] = eunit;
  }

  void pushup(int p, int sz) {
    if (p >= n) return;
    data[p] = merge(data[p << 1], data[p << 1 | 1]);
    if (lazy[p] != eunit) data[p] = upd(data[p], lazy[p], sz);
  }

  void flush(int l, int r) {
    int s = h, k = 1 << h;
    for (l += n, r += n - 1; s > 0; s--, k >>= 1)
      for (int p = l >> s; p <= r >> s; p++)
        pushdown(p, k);
  }

  void build(int l, int r) {
    int sz = 2;
    for (l += n, r += n - 1; l > 1; sz <<= 1) {
      l >>= 1, r >>= 1;
      for (int p = l; p <= r; p++) pushup(p, sz);
    }
  }

  template <typename Iter>
  void assign(Iter first, Iter last) {
    copy(first, last, data.begin() + n);
    build(0, n);
  }

 public:
  void modify(int l, int r, U e) {
    if (e == eunit) return;
    flush(l, l + 1);
    flush(r - 1, r);
    int l0 = l, r0 = r, k = 1;
    for (l += n, r += n; l < r; l >>= 1, r >>= 1, k <<= 1) {
      if (l & 1) apply(l++, e, k);
      if (r & 1) apply(--r, e, k);
    }
    build(l0, l0 + 1);
    build(r0 - 1, r0);
  }

  T query(int l, int r) {
    flush(l, l + 1);
    flush(r - 1, r);
    T resl = unit, resr = unit;
    for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
      if (l & 1) resl = merge(resl, data[l++]);
      if (r & 1) resr = merge(data[--r], resr);
    }
    return merge(resl, resr);
  }
};
#pragma GCC diagnostic warning "-Wshadow"

#pragma GCC diagnostic ignored "-Wunused-parameter"
struct minT {
  template <typename T>
  T operator()(T a, T b) const { return min(a, b); }
};

struct maxT {
  template <typename T>
  T operator()(T a, T b) const { return max(a, b); }
};

struct assignT {
  template <typename T>
  T operator()(T a, T b, int k = 0) const { return b; }
};
#pragma GCC diagnostic ignored "-Wunused-parameter"

struct plusT {
  template <typename T>
  T operator()(T a, T b, int k) const { return a + b * k; }
};


namespace rolling_hash {

constexpr ull mask30 = (1ULL << 30) - 1;
constexpr ull mask31 = (1ULL << 31) - 1;
constexpr ull MOD = (1ULL << 61) - 1;
int base = rand();
vector<ull> pows{1};

ull mod(ull val) {
  val = (val & MOD) + (val >> 61);
  return val >= MOD ? val - MOD : val;
}

ull mul(ull l, ull r) {
  ull lu = l >> 31, ld = l & mask31;
  ull ru = r >> 31, rd = r & mask31;
  ull middle = ld * ru + lu * rd;
  return ((lu * ru) << 1) + ld * rd + ((middle & mask30) << 31) +
         (middle >> 30);
}

ull mul(ull l, int r) {
  ull lu = l >> 31, ld = l & mask31;
  ull middle = lu * r;
  return ld * r + ((middle & mask30) << 31) + (middle >> 30);
}

void prepare_pows(size_t sz) {
  rep(i, pows.size() - 1, sz - 1) pows.push_back(mod(mul(pows[i], base)));
}

class Calculator {
 private:
  vector<ull> hash;

 public:
  template <typename Iter>
  Calculator(Iter first, Iter last) : hash(last - first + 1) {
    prepare_pows(last - first + 1);
    rep(i, last - first) hash[i + 1] = mod(mul(hash[i], base) + first[i]);
    // assert(hash[i+1] < (1ULL << 62));
  }

 public:
  ull operator()(int l, int r) const {
    static constexpr ull large = MOD * ((1 << 2) - 1);
    return mod(hash[r] + large - mul(hash[l], pows[r - l]));
  }
};

}  // namespace rolling_hash


namespace hash_monoid {

using namespace rolling_hash;

vector<ull> sum_pows{1};

void prepare_sum_pows(size_t sz) {
  prepare_pows(sz);
  rep(i, sum_pows.size() - 1, sz - 1) {
    sum_pows.push_back(mod(sum_pows[i] + pows[i + 1]));
  }
}

// monoid
struct Hash {
  ull value;
  int length;

  Hash() : value(0), length(0) {} // unit
  Hash(ull _value, int _length) : value(_value), length(_length) {}
  Hash(char c, int _length = 1) : value(calc_hash(c, _length)), length(_length) {}

 private:
  ull calc_hash(char c, int _length) {
    prepare_sum_pows(_length);
    return mod(mul(sum_pows[_length - 1], c));
  }

 public:
  operator ull() { return value; }
  bool operator==(const Hash &rhs) {
    return value == rhs.value && length == rhs.length;
  }
  bool operator!=(const Hash &rhs) {
    return value == rhs.value && length == rhs.length;
  }
};


struct mergeT {
  Hash operator()(const Hash &lhs, const Hash &rhs) const {
    prepare_pows(rhs.length+1);
    return Hash(mod(mul(lhs.value, pows[rhs.length]) + rhs.value),
                lhs.length + rhs.length);
  }
};

#pragma GCC diagnostic ignored "-Wunused-parameter"
struct updT {
  Hash operator()(const Hash &lhs, char c, int k = 1) const {
    return Hash(c, k);
  }
};
#pragma GCC diagnostic warning "-Wunused-parameter"

// using HashSegTree = SegmentTree<Hash, mergeT, updT>;
using HashSegTree = LazySegmentTree<Hash, char, mergeT, assignT, updT>;

}  // namespace hash_monoid



int main() {
  int n = IN;
  string u = IN;

  hash_monoid::HashSegTree hash(all(u), n);

  rep(IN) {
    string cmd = IN;
    if (cmd[0] == 's') {
      int x = IN1, y = IN1; string z = IN;
      hash.modify(x, y+1, z[0]);
    } else {
      int a = IN1, b = IN1, c = IN1, d = IN1;
      b++; d++;
      int eq = 0, neq = min(b-a, d-c)+1;
      while (neq - eq > 1) {
        int m = (eq + neq) / 2;
        auto l = hash.query(a, a+m);
        auto r = hash.query(c, c+m);
        (l == r ? eq : neq) = m;
      }
      if (eq == b-a and eq == d-c) OUT("e");
      else if (eq == b-a) OUT("s");
      else if (eq == d-c) OUT("t");
      else {
        auto s = hash.query(a+eq, a+eq+1).value;
        auto t = hash.query(c+eq, c+eq+1).value;
        OUT(s < t ? "s" : "t");
      }
    }
  }
}
accuracy: 1
time_ms: 910
memory_kb: 12620
final_rank: 5

code_snippet: #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <cstdint>
using namespace std;
using llong = long long;

//===
template<typename Monoid, typename Laz>
struct LazySegmentTree {

    const function<Monoid(Monoid, Monoid)> mergeMonoid;
    const function<Monoid(Monoid, Laz, int)> applyLaz;
    const function<Laz(Laz, Laz)> mergeLaz;
    
    const Monoid e; // neutral element
    
    vector<Monoid> seg;
    vector<Laz> lazy;
    vector<bool> isUpdated;
    
    int size;
                                           
    LazySegmentTree(int nmemb, const Monoid &e,
                    function<Monoid(Monoid, Monoid)> f,
                    function<Monoid(Monoid, Laz, int)> g,
                    function<Laz(Laz, Laz)> h):
        e(e), mergeMonoid(f), applyLaz(g), mergeLaz(h)
    {
        size = 1;
        while (size < nmemb) {
            size *= 2;
        }

        seg.assign(2 * size - 1, e);
        isUpdated.assign(2 * size - 1, true);
        lazy.resize(2 * size - 1); 
    }

    inline void propagation(int k, int len) {
        if (!isUpdated[k]) {
            seg[k] = applyLaz(seg[k], lazy[k], len);
            if (len > 1) {
                if (isUpdated[2 * k + 1])
                    lazy[2 * k + 1] = lazy[k], isUpdated[2 * k + 1] = false;
                else
                    lazy[2 * k + 1] = mergeLaz(lazy[2 * k + 1], lazy[k]);
                
                if (isUpdated[2 * k + 2])
                    lazy[2 * k + 2] = lazy[k], isUpdated[2 * k + 2] = false;
                else
                    lazy[2 * k + 2] = mergeLaz(lazy[2 * k + 2], lazy[k]);
            }
            isUpdated[k] = true;
        }
    }

    Monoid update(int k, int nl, int nr, int ql, int qr, Laz dat) {
        propagation(k, nr - nl);

        if (nr <= ql || qr <= nl) return seg[k];

        if (ql <= nl && nr <= qr) {
            lazy[k] = dat;
            isUpdated[k] = false;
            propagation(k, nr - nl);
            return seg[k];
        }
        else {
            seg[k] = mergeMonoid(update(2 * k + 1, nl, (nl + nr) / 2, ql, qr, dat),
                                 update(2 * k + 2, (nl + nr) / 2, nr, ql, qr, dat));
            return seg[k];
        }
    }

    // [l, r) <= dat
    void update(int l, int r, Laz dat) {
        update(0, 0, size, l, r, dat);
    }

    Monoid fold(int k, int nl, int nr, int ql, int qr) {

        propagation(k, nr - nl);
        
        if (nr <= ql || qr <= nl) return e;

        if (ql <= nl && nr <= qr) return seg[k];
        else return mergeMonoid(fold(2 * k + 1, nl, (nl + nr) / 2, ql, qr),
                                fold(2 * k + 2, (nl + nr) / 2, nr, ql, qr));
    }

    // [l, r)
    Monoid fold(int l, int r) {
        if (r <= l) return e;
        return fold(0, 0, size, l, r);
    }

    Monoid operator [](const int &k) {
        return fold(k, k + 1);
    }
};
//===

template<uint64_t mod, uint64_t base>
struct RollingHashMonoid {
    using rh = RollingHashMonoid<mod, base>;
        
    int sz;
    uint64_t h;

    static vector<uint64_t> p;

    RollingHashMonoid() {};
    RollingHashMonoid(uint64_t ch): sz(1), h(ch % mod) {};
    
    static rh neutral() {
        RollingHashMonoid res;
        res.sz = 0, res.h = 0;
        return res;
    };

    rh operator + (const rh &r) const {
        const rh &l = *this;
        rh res = neutral();

        while (p.size() <= r.sz) p.push_back(p.back() * base % mod);

        res.sz = l.sz + r.sz;
        res.h = (l.h * p[r.sz] % mod + r.h) % mod;
        return res;
    };
};
template<uint64_t mod, uint64_t base>
vector<uint64_t> RollingHashMonoid<mod, base>::p = vector<uint64_t>(1, 1);
//===

const uint64_t base = 100;
const uint64_t mod  = (uint64_t)(1e9) + 9;
using rh = RollingHashMonoid<mod, base>;

uint64_t pow_t[200005];
LazySegmentTree<rh, char> hash_str(200005, rh::neutral(),
                                   [](rh l, rh r){
                                       return l + r;
                                   },
                                   [](rh l, char r, int len){
                                       rh res(r * pow_t[len] % mod);
                                       res.sz = len;
                                       return res;
                                   },
                                   [](char l, char r) {
                                       return r;
                                   } );

int main() {
    cin.tie(0);
    ios::sync_with_stdio(0);
    //init
    {
        uint64_t b = 1;
        pow_t[0] = pow_t[1] = 1;
        for (int i = 2; i <= 200003; i++) {
            b = b * base % mod;
            pow_t[i] = (pow_t[i - 1] + b) % mod;
        }
    }

    llong n;
    llong q;
    string u;
    string com;

    cin >> n >> u >> q;

    for (int i = 0; i < n; i++) {
        hash_str.update(i, i + 1, u[i]);
    }

    while (q--) {
        llong a, b, c, d;
        llong x, y;
        char z;
        
        cin >> com;
        if (com == "set") {
            cin >> x >> y >> z;
            hash_str.update(x - 1, y, z);
        }
        else {
            cin >> a >> b >> c >> d;
            --a, --c;

            llong valid = 0;
            llong invalid = min(b - a, d - c) + 1;
            llong mid;

            while (invalid - valid > 1) {
                mid = (valid + invalid) / 2;
                if (hash_str.fold(a, a + mid).h ==
                    hash_str.fold(c, c + mid).h) {
                    valid = mid;
                }
                else {
                    invalid = mid;
                }
            }
            
            if (valid == min(b - a, d - c)) {
                if (b - a < d - c) {
                    cout << "s\n";
                }
                else if (b - a == d - c) {
                    cout << "e\n";
                }
                else {
                    cout << "t\n";
                }
            }
            else {
                if (hash_str[a + valid].h < hash_str[c + valid].h) {
                    cout << "s\n";
                }
                else {
                    cout << "t\n";
                }
            }
        }
    }

    return 0;
}
accuracy: 1
time_ms: 1440
memory_kb: 14432
final_rank: 9

code_snippet: #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <cstdint>
using namespace std;
using llong = long long;

//===
template<typename Monoid, typename Laz>
struct LazySegmentTree {

    const function<Monoid(Monoid, Monoid)> mergeMonoid;
    const function<Monoid(Monoid, Laz, int)> applyLaz;
    const function<Laz(Laz, Laz)> mergeLaz;
    
    const Monoid e; // neutral element
    
    vector<Monoid> seg;
    vector<Laz> lazy;
    vector<bool> isUpdated;
    
    int size;
                                           
    LazySegmentTree(int nmemb, const Monoid &e,
                    function<Monoid(Monoid, Monoid)> f,
                    function<Monoid(Monoid, Laz, int)> g,
                    function<Laz(Laz, Laz)> h):
        e(e), mergeMonoid(f), applyLaz(g), mergeLaz(h)
    {
        size = 1;
        while (size < nmemb) {
            size *= 2;
        }

        seg.assign(2 * size - 1, e);
        isUpdated.assign(2 * size - 1, true);
        lazy.resize(2 * size - 1); 
    }

    inline void propagation(int k, int len) {
        if (!isUpdated[k]) {
            seg[k] = applyLaz(seg[k], lazy[k], len);
            if (len > 1) {
                if (isUpdated[2 * k + 1])
                    lazy[2 * k + 1] = lazy[k], isUpdated[2 * k + 1] = false;
                else
                    lazy[2 * k + 1] = mergeLaz(lazy[2 * k + 1], lazy[k]);
                
                if (isUpdated[2 * k + 2])
                    lazy[2 * k + 2] = lazy[k], isUpdated[2 * k + 2] = false;
                else
                    lazy[2 * k + 2] = mergeLaz(lazy[2 * k + 2], lazy[k]);
            }
            isUpdated[k] = true;
        }
    }

    Monoid update(int k, int nl, int nr, int ql, int qr, Laz dat) {
        propagation(k, nr - nl);

        if (nr <= ql || qr <= nl) return seg[k];

        if (ql <= nl && nr <= qr) {
            lazy[k] = dat;
            isUpdated[k] = false;
            propagation(k, nr - nl);
            return seg[k];
        }
        else {
            seg[k] = mergeMonoid(update(2 * k + 1, nl, (nl + nr) / 2, ql, qr, dat),
                                 update(2 * k + 2, (nl + nr) / 2, nr, ql, qr, dat));
            return seg[k];
        }
    }

    // [l, r) <= dat
    void update(int l, int r, Laz dat) {
        update(0, 0, size, l, r, dat);
    }

    Monoid fold(int k, int nl, int nr, int ql, int qr) {

        propagation(k, nr - nl);
        
        if (nr <= ql || qr <= nl) return e;

        if (ql <= nl && nr <= qr) return seg[k];
        else return mergeMonoid(fold(2 * k + 1, nl, (nl + nr) / 2, ql, qr),
                                fold(2 * k + 2, (nl + nr) / 2, nr, ql, qr));
    }

    // [l, r)
    Monoid fold(int l, int r) {
        if (r <= l) return e;
        return fold(0, 0, size, l, r);
    }

    Monoid operator [](const int &k) {
        return fold(k, k + 1);
    }
};
//===

template<uint64_t mod, uint64_t base>
struct RollingHashMonoid {
    using rh = RollingHashMonoid<mod, base>;
        
    int sz;
    uint64_t h;

    static vector<uint64_t> p;

    RollingHashMonoid() {};
    RollingHashMonoid(uint64_t ch): sz(1), h(ch % mod) {};
    
    static rh neutral() {
        RollingHashMonoid res;
        res.sz = 0, res.h = 0;
        return res;
    };

    rh operator + (const rh &r) const {
        const rh &l = *this;
        rh res = neutral();

        while (p.size() <= r.sz) p.push_back(p.back() * base % mod);

        res.sz = l.sz + r.sz;
        res.h = (l.h * p[r.sz] % mod + r.h) % mod;
        return res;
    };
};
template<uint64_t mod, uint64_t base>
vector<uint64_t> RollingHashMonoid<mod, base>::p = vector<uint64_t>(1, 1);
//===

const uint64_t base = 100;
const uint64_t mod  = (uint64_t)(1e9) + 9;
using rh = RollingHashMonoid<mod, base>;

uint64_t pow_t[200005];
LazySegmentTree<rh, char> hash_str(200005, rh::neutral(),
                                   [](rh l, rh r){
                                       return l + r;
                                   },
                                   [](rh l, char r, int len){
                                       rh res(r * pow_t[len] % mod);
                                       res.sz = len;
                                       return res;
                                   },
                                   [](char l, char r) {
                                       return r;
                                   } );

int main() {
    //init
    {
        uint64_t b = 1;
        pow_t[0] = pow_t[1] = 1;
        for (int i = 2; i <= 200003; i++) {
            b = b * base % mod;
            pow_t[i] = (pow_t[i - 1] + b) % mod;
        }
    }

    llong n;
    llong q;
    string u;
    string com;

    cin >> n >> u >> q;

    for (int i = 0; i < n; i++) {
        hash_str.update(i, i + 1, u[i]);
    }

    while (q--) {
        llong a, b, c, d;
        llong x, y;
        char z;
        
        cin >> com;
        if (com == "set") {
            cin >> x >> y >> z;
            hash_str.update(x - 1, y, z);
        }
        else {
            cin >> a >> b >> c >> d;
            --a, --c;

            llong valid = 0;
            llong invalid = min(b - a, d - c) + 1;
            llong mid;

            while (invalid - valid > 1) {
                mid = (valid + invalid) / 2;
                if (hash_str.fold(a, a + mid).h ==
                    hash_str.fold(c, c + mid).h) {
                    valid = mid;
                }
                else {
                    invalid = mid;
                }
            }
            
            if (valid == min(b - a, d - c)) {
                if (b - a < d - c) {
                    cout << "s\n";
                }
                else if (b - a == d - c) {
                    cout << "e\n";
                }
                else {
                    cout << "t\n";
                }
            }
            else {
                if (hash_str[a + valid].h < hash_str[c + valid].h) {
                    cout << "s\n";
                }
                else {
                    cout << "t\n";
                }
            }
        }
    }

    return 0;
}
accuracy: 1
time_ms: 1620
memory_kb: 14544
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()
#define let auto const

struct tt {
  i64 val;
  i64 base;
};

struct lazy_segment_tree {
  using T = tt;
  using L = i64;
  const i64 mod = 1e9 + 7;
  const i64 base = 9973;
  T t_ide() { return { 0, 1 }; }
  L l_ide() { return 0; }
  T ope(const T& a, const T& b) {
    return { (a.val * b.base + b.val) % mod, a.base * b.base % mod };
  }
  L lazy_ope(const L& a, const L& b) { return b; }
  T effect(const T& t, const L& l, const i64 len) { return { l * bsum[len] % mod, t.base }; }

  i64 n;
  vector<T> node;
  vector<L> lazy;
  vector<bool> flag;
  vector<i64> bsum;

  /* create lazy_segment_tree for sequence [t_ide; sz]A */
  lazy_segment_tree(std::vector<i64> init) {
    n = 1;
    while(n < init.size()) n *= 2;
    node.assign(n * 2, t_ide());
    lazy.assign(n * 2, l_ide());
    flag.assign(n * 2, false);
    for(int i = 0;i < init.size();i++) node[i + n - 1] = { init[i], base };
    for(int i = n - 2;i >= 0;i--) {
      node[i] = ope(node[i * 2 + 1], node[i * 2 + 2]);
    }
    bsum.assign(n + 1, 0);
    for(int i = 1;i <= n;i++) bsum[i] = (bsum[i - 1] * base + 1) % mod;
  }
  

  void eval(i64 l, i64 r, i64 k) {
    if(flag[k]) {
      node[k] = effect(node[k], lazy[k], r - l);
      
      if(r - l > 1) {
        lazy[k * 2 + 1] = lazy_ope(lazy[k * 2 + 1], lazy[k]);
        flag[k * 2 + 1] = true;
        lazy[k * 2 + 2] = lazy_ope(lazy[k * 2 + 2], lazy[k]);
        flag[k * 2 + 2] = true;
      }

      flag[k] = false;
      lazy[k] = l_ide();
    }
  }
  
  /* for i in [a, b) effect(a[i], lx) */
  void update(i64 a, i64 b, L lx, i64 l = 0, i64 r = -1, i64 k = 0) {
    if(r < 0) r = n;
    eval(l, r, k);
    if(b <= l || r <= a) return;
    else if(a <= l && r <= b) {
      lazy[k] = lazy_ope(lazy[k], lx);
      flag[k] = true;
      eval(l, r, k);
    }
    else {
      update(a, b, lx, l, (l + r) / 2, k * 2 + 1);
      update(a, b, lx, (l + r) / 2, r, k * 2 + 2);
      node[k] =ope(node[k * 2 + 1], node[k * 2 + 2]);
    }
  }
  
  /* the sum of a[i] for i in [a, b) */
  T get(i64 a, i64 b, i64 l = 0, i64 r = -1, i64 k = 0) {
    if(r < 0) r = n;
    eval(l, r, k);
    if(b <= l || r <= a) return t_ide();
    else if(a <= l && r <= b) return node[k];
    else return ope(get(a, b, l, (l + r) / 2, k * 2 + 1), get(a, b, (l + r) / 2, r, k * 2 + 2));
  }
};

int main() {
  i64 N;
  cin >> N;
  string U;
  cin >> U;
  vector<i64> u(N);
  rep(i,0,N) u[i] = (i64)(U[i] - 'a');

  lazy_segment_tree seg(u);
  i64 Q;
  cin >> Q;

  rep(q,0,Q) {
    string que;
    cin >> que;
    if(que == "set") {
      i64 x, y;
      char z;
      cin >> x >> y >> z;
      seg.update(x - 1, y, (i64)(z - 'a'));
    }
    else {
      i64 a, b, c, d;
      cin >> a >> b >> c >> d;
      a--;
      c--;
      i64 OK = 0;
      i64 NG = min(b - a, d - c) + 1;
      while(NG - OK > 1) {
        i64 mid = (OK + NG) / 2;
        //cout << mid << endl;
        //cout << a << " " << a + mid << " = " << seg.get(a, a + mid).val << endl;
        //cout << c << " " << c + mid << " = " << seg.get(c , c + mid).val << endl;
        if(seg.get(a, a + mid).val == seg.get(c, c + mid).val) {
          OK = mid;
        }
        else {
          NG = mid;
        }
      }
      if(a + OK == b && c + OK == d) {
        cout << "e" << endl;
      }
      else if(a + OK == b) {
        cout << "s" << endl;
      }
      else if(c + OK == d) {
        cout << "t" << endl;
      }
      else if(seg.get(a + OK, a + OK + 1).val < seg.get(c + OK, c + OK + 1).val) {
        cout << "s" << endl;
      }
      else {
        cout << "t" << endl;
      }
    }
  }
}
accuracy: 1
time_ms: 2260
memory_kb: 20684
final_rank: 16

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define SIZE 200005
#define NUM1 1000000007
#define NUM2 99999883


struct Data{
	Data(){

		hash_1 = 0;
		hash_2 = 0;
		length = 0;
	}
	Data(ull arg_hash_1,ull arg_hash_2,ull arg_length){
		hash_1 = arg_hash_1;
		hash_2 = arg_hash_2;
		length = arg_length;
	}

	ull hash_1,hash_2;
	ull length;
};

int N;
int LENGTH;
char buf[SIZE];
int data[8*SIZE]; //文字を持つ
ull HASH[8*SIZE][2];
ull POW[SIZE][2],TABLE[26][SIZE][2];

void init(int first_N){
	while(N < first_N)N *= 2;
}

void update(int update_left,int update_right,int new_value,int node_id,int node_left,int node_right){
	if(update_right < node_left || update_left > node_right)return; //区間に重なりがなければreturn
	else if(update_left <= node_left && update_right >= node_right){ //このノードがカバーしている区間が、更新区間の部分区間である場合

		data[node_id] = new_value; //文字('a'からの差)
		HASH[node_id][0] = TABLE[new_value][node_right-node_left+1][0];
		HASH[node_id][1] = TABLE[new_value][node_right-node_left+1][1];

	}else{ //一部に重なりがある場合
		//データを左右の子に引き継ぐ
		if(data[node_id] >= 0){
			update(node_left,node_right,data[node_id],2*node_id+1,node_left,(node_left+node_right)/2);
			update(node_left,node_right,data[node_id],2*node_id+2,(node_left+node_right)/2+1,node_right);
			data[node_id] = -1; //値を持っていないことを表す
		}
		update(update_left,update_right,new_value,2*node_id+1,node_left,(node_left+node_right)/2);
		update(update_left,update_right,new_value,2*node_id+2,(node_left+node_right)/2+1,node_right);

		ull right_len = node_right-(node_left+node_right)/2;

		HASH[node_id][0] = HASH[2*node_id+1][0]*POW[right_len][0]+HASH[2*node_id+2][0];
		HASH[node_id][1] = HASH[2*node_id+1][1]*POW[right_len][1]+HASH[2*node_id+2][1];
	}
}

//ハッシュ値および区間の長さを返却する
Data query(int search_left,int search_right,int node_id,int node_left,int node_right){

	if(search_right < node_left || search_left > node_right){
		return Data(0,0,0);
	}else if(node_left >= search_left && node_right <= search_right){
		return Data(HASH[node_id][0],HASH[node_id][1],(ull)(node_right-node_left+1));
	}else{

		if(data[node_id] >= 0){
			update(node_left,node_right,data[node_id],2*node_id+1,node_left,(node_left+node_right)/2);
			update(node_left,node_right,data[node_id],2*node_id+2,(node_left+node_right)/2+1,node_right);
			data[node_id] = -1; //値を持っていないことを表す
			ull right_len = node_right-(node_left+node_right)/2;

			HASH[node_id][0] = HASH[2*node_id+1][0]*POW[right_len][0]+HASH[2*node_id+2][0];
			HASH[node_id][1] = HASH[2*node_id+1][1]*POW[right_len][1]+HASH[2*node_id+2][1];
		}

		Data left = query(search_left,search_right,2*node_id+1,node_left,(node_left+node_right)/2);
		Data right = query(search_left,search_right,2*node_id+2,(node_left+node_right)/2+1,node_right);

		return Data(left.hash_1*POW[right.length][0]+right.hash_1,left.hash_2*POW[right.length][1]+right.hash_2,left.length+right.length);
	}
}

int find_word(Data info){

	for(int i = 0; i < 26; i++){

		if(info.hash_1 == TABLE[i][1][0] && info.hash_2 == TABLE[i][1][1]){
			return i;
		}
	}
	return -1;
}

int main(){

	scanf("%d",&LENGTH);

	//POWテーブル作成
	POW[0][0] = 1;
	POW[0][1] = 1;

	for(int i = 1; i <= LENGTH; i++){

		POW[i][0] = POW[i-1][0]*NUM1;
		POW[i][1] = POW[i-1][1]*NUM2;
	}

	//文字のハッシュ値をあらかじめ作っておく
	for(int i = 0; i < 26; i++){
		TABLE[i][0][0] = 0;
		TABLE[i][0][1] = 0;
		for(int k = 1; k <= LENGTH; k++){
			TABLE[i][k][0] = ('a'+i) + TABLE[i][k-1][0]*NUM1;
			TABLE[i][k][1] = ('a'+i) + TABLE[i][k-1][1]*NUM2;
		}
	}

	N = 1;
	init(LENGTH);
	for(int i = 0; i <= 2*N-2; i++)data[i] = -1;

	scanf("%s",buf);
	for(int i = 0; i < LENGTH; i++){
		update(i,i,buf[i]-'a',0,0,N-1);
	}

	int num_query;
	scanf("%d",&num_query);

	char order[6],z[2];
	int x,y,a,b,c,d;
	int L,R,mid,match_len;

	Data A,B;

	for(int loop = 0; loop < num_query; loop++){

		scanf("%s",order);

		if(order[0] == 's'){

			scanf("%d %d %s",&x,&y,z);
			update(x-1,y-1,z[0]-'a',0,0,N-1);

		}else{ //比較命令

			scanf("%d %d %d %d",&a,&b,&c,&d);

			a--;
			b--;
			c--;
			d--;

			L = 0,R = min(b-a+1,d-c+1),mid = (L+R)/2;
			match_len = 0;

			while(L <= R){

				A = query(a,a+mid-1,0,0,N-1);
				B = query(c,c+mid-1,0,0,N-1);

				if(A.hash_1==B.hash_1 && A.hash_2 == B.hash_2){

					match_len = mid;
					L = mid+1;

				}else{

					R = mid-1;
				}
				mid = (L+R)/2;
			}

			if(match_len == b-a+1 && match_len == d-c+1){

				printf("e\n");
			}else{
				if(match_len == b-a+1 || (match_len != d-c+1 && find_word(query(a+match_len,a+match_len,0,0,N-1)) < find_word(query(c+match_len,c+match_len,0,0,N-1)))){

					printf("s\n");
				}else{
					printf("t\n");
				}
			}
		}
	}

	return 0;
}
accuracy: 1
time_ms: 1040
memory_kb: 96032
final_rank: 17

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define SIZE 200005
#define NUM1 1000000007
#define NUM2 99999883


struct Data{
	Data(){

		hash_1 = 0;
		hash_2 = 0;
		length = 0;
	}
	Data(ull arg_hash_1,ull arg_hash_2,ull arg_length){
		hash_1 = arg_hash_1;
		hash_2 = arg_hash_2;
		length = arg_length;
	}

	ull hash_1,hash_2;
	ull length;
};

int N;
int LENGTH;
char buf[SIZE];
int data[8*SIZE]; //文字を持つ
ull HASH[8*SIZE][2];
ull POW[SIZE][2],TABLE[26][SIZE][2];

void init(int first_N){
	while(N < first_N)N *= 2;
}

void update(int update_left,int update_right,int new_value,int node_id,int node_left,int node_right){
	if(update_right < node_left || update_left > node_right)return; //区間に重なりがなければreturn
	else if(update_left <= node_left && update_right >= node_right){ //このノードがカバーしている区間が、更新区間の部分区間である場合

		data[node_id] = new_value; //文字('a'からの差)
		HASH[node_id][0] = TABLE[new_value][node_right-node_left+1][0];
		HASH[node_id][1] = TABLE[new_value][node_right-node_left+1][1];

	}else{ //一部に重なりがある場合
		//データを左右の子に引き継ぐ
		if(data[node_id] >= 0){
			update(node_left,node_right,data[node_id],2*node_id+1,node_left,(node_left+node_right)/2);
			update(node_left,node_right,data[node_id],2*node_id+2,(node_left+node_right)/2+1,node_right);
			data[node_id] = -1; //値を持っていないことを表す
		}
		update(update_left,update_right,new_value,2*node_id+1,node_left,(node_left+node_right)/2);
		update(update_left,update_right,new_value,2*node_id+2,(node_left+node_right)/2+1,node_right);

		ull right_len = node_right-(node_left+node_right)/2;

		HASH[node_id][0] = HASH[2*node_id+1][0]*POW[right_len][0]+HASH[2*node_id+2][0];
		HASH[node_id][1] = HASH[2*node_id+1][1]*POW[right_len][1]+HASH[2*node_id+2][1];
	}
}

//ハッシュ値および区間の長さを返却する
Data query(int search_left,int search_right,int node_id,int node_left,int node_right){

	if(data[node_id] >= 0){
		update(node_left,node_right,data[node_id],2*node_id+1,node_left,(node_left+node_right)/2);
		update(node_left,node_right,data[node_id],2*node_id+2,(node_left+node_right)/2+1,node_right);
		data[node_id] = -1; //値を持っていないことを表す
		ull right_len = node_right-(node_left+node_right)/2;

		HASH[node_id][0] = HASH[2*node_id+1][0]*POW[right_len][0]+HASH[2*node_id+2][0];
		HASH[node_id][1] = HASH[2*node_id+1][1]*POW[right_len][1]+HASH[2*node_id+2][1];
	}

	if(search_right < node_left || search_left > node_right){
		return Data(0,0,0);
	}else if(node_left >= search_left && node_right <= search_right){
		return Data(HASH[node_id][0],HASH[node_id][1],(ull)(node_right-node_left+1));
	}else{

		Data left = query(search_left,search_right,2*node_id+1,node_left,(node_left+node_right)/2);
		Data right = query(search_left,search_right,2*node_id+2,(node_left+node_right)/2+1,node_right);

		return Data(left.hash_1*POW[right.length][0]+right.hash_1,left.hash_2*POW[right.length][1]+right.hash_2,left.length+right.length);
	}
}

int find_word(Data info){

	for(int i = 0; i < 26; i++){

		if(info.hash_1 == TABLE[i][1][0] && info.hash_2 == TABLE[i][1][1]){
			return i;
		}
	}
	return -1;
}

int main(){

	scanf("%d",&LENGTH);

	//POWテーブル作成
	POW[0][0] = 1;
	POW[0][1] = 1;

	for(int i = 1; i <= LENGTH; i++){

		POW[i][0] = POW[i-1][0]*NUM1;
		POW[i][1] = POW[i-1][1]*NUM2;
	}

	//文字のハッシュ値をあらかじめ作っておく
	for(int i = 0; i < 26; i++){
		TABLE[i][0][0] = 0;
		TABLE[i][0][1] = 0;
		for(int k = 1; k <= LENGTH; k++){
			TABLE[i][k][0] = ('a'+i) + TABLE[i][k-1][0]*NUM1;
			TABLE[i][k][1] = ('a'+i) + TABLE[i][k-1][1]*NUM2;
		}
	}

	N = 1;
	init(LENGTH);
	for(int i = 0; i <= 2*N-2; i++)data[i] = -1;

	scanf("%s",buf);
	for(int i = 0; i < LENGTH; i++){
		update(i,i,buf[i]-'a',0,0,N-1);
	}

	int num_query;
	scanf("%d",&num_query);

	char order[6],z[2];
	int x,y,a,b,c,d;
	int L,R,mid,match_len;

	Data A,B;

	for(int loop = 0; loop < num_query; loop++){

		scanf("%s",order);

		if(order[0] == 's'){

			scanf("%d %d %s",&x,&y,z);
			update(x-1,y-1,z[0]-'a',0,0,N-1);

		}else{ //比較命令

			scanf("%d %d %d %d",&a,&b,&c,&d);

			a--;
			b--;
			c--;
			d--;

			L = 0,R = min(b-a+1,d-c+1),mid = (L+R)/2;
			match_len = 0;

			while(L <= R){

				A = query(a,a+mid-1,0,0,N-1);
				B = query(c,c+mid-1,0,0,N-1);

				if(A.hash_1==B.hash_1 && A.hash_2 == B.hash_2){

					match_len = mid;
					L = mid+1;

				}else{

					R = mid-1;
				}
				mid = (L+R)/2;
			}

			if(match_len == b-a+1 && match_len == d-c+1){

				printf("e\n");
			}else{
				if(match_len == b-a+1 || (match_len != d-c+1 && find_word(query(a+match_len,a+match_len,0,0,N-1)) < find_word(query(c+match_len,c+match_len,0,0,N-1)))){

					printf("s\n");
				}else{
					printf("t\n");
				}
			}
		}
	}

	return 0;
}
accuracy: 1
time_ms: 1060
memory_kb: 102388
final_rank: 19

code_snippet: # include <iostream>
# include <algorithm>
#include <array>
# include <cassert>
#include <cctype>
#include <climits>
#include <numeric>
# include <vector>
# include <string>
# include <set>
# include <map>
# include <cmath>
# include <iomanip>
# include <functional>
# include <tuple>
# include <utility>
# include <stack>
# include <queue>
# include <list>
# include <bitset>
# include <complex>
# include <chrono>
# include <random>
# include <limits.h>
# include <unordered_map>
# include <unordered_set>
# include <deque>
# include <cstdio>
# include <cstring>
#include <stdio.h>
#include<time.h>
#include <stdlib.h>
#include <cstdint>
#include <cfenv>
#include<fstream>
//#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
//long long MOD = 1000000000 + 7;//1000000000 + 7 998244353 924844033 1000000000 + 9;
constexpr long long INF = 1LL << 60;//numeric_limits<LL>::max();
const double PI = acos(-1);
#define fir first
#define sec second
#define thi third
#define debug(x) cerr<<#x<<": "<<x<<'\n'
typedef pair<LL, LL> Pll;
typedef pair<double, double> Dll;
typedef pair<LL, pair<LL, LL>> Ppll;
typedef pair<LL, pair<LL, bitset<100001>>> Pbll;
typedef pair<LL, pair<LL, vector<LL>>> Pvll;
typedef pair<LL, LL> Vec2;
struct Tll { LL first, second, third; };
struct Fll { LL first, second, third, fourth; };
typedef pair<LL, Tll> Ptll;
#define rep(i,rept) for(LL i=0;i<rept;i++)
#define Rrep(i,mf) for(LL i=mf-1;i>=0;i--)
void YN(bool f) {
	if (f)
		cout << "YES" << endl;
	else
		cout << "NO" << endl;
}
void yn(bool f) {
	if (f)
		cout << "Yes" << endl;
	else
		cout << "No" << endl;
}
struct Edge { LL to, cost; };
struct edge {
	LL from, to, cost;
};
vector<vector<LL>>g;
vector<edge>ed;
vector<Pll>pv;
set<LL>st;
map<Pll, LL>ma;
int di[4][2] = { { 0,1 },{ 1,0 },{ 0,-1 },{ -1,0 } };
string str, ss;
bool f;
LL n, m, s, t, h, w, k,q, ans,sum, cnt,a[20];

const int P = 1685440109;
const int MOD = 2000000087;
class SegmentTree{
private:
	static const LL INIT_DATA=0;
	static const char INIT_DELAY = '\0';
	LL updateData(LL prev, int len, LL x) {
		//現在の範囲がすべて文字xで更新された場合
		if (x == INIT_DELAY)
			return prev;
		else
			return x * pPowSum[len] % MOD;
	}
	char updateDelay(char x1, char x2) {
		//x1にx2を伝播する
		if (x2 == INIT_DELAY)
			return x1;
		else
			return x2;
	}
	LL uniteData(LL v1, int len1, LL v2, int len2) {
		return (v1 + v2 * pPow[len1]) % MOD;
	}
	vector<long long> pPow, pPowSum;
	int n;
	vector<LL> node;
	vector<char> delay;
	void updateTree(int a, int b, int k, int l, int r, char x) {
		//[l,r]
		if (a <= l && r <= b) {
			node[k] = updateData(node[k], r - l + 1, x);
			delay[k] = updateDelay(delay[k], x);
		}
		else if (a <= r && l <= b) {
			int len = (r - l + 1) / 2;
			for (int i = 0; i<2; ++i) {
				node[k * 2 + 1 + i] = updateData(node[k * 2 + 1 + i], len, delay[k]);
				delay[k * 2 + 1 + i] = updateDelay(delay[k * 2 + 1 + i], delay[k]);
			}
			delay[k] = INIT_DELAY;
			updateTree(a, b, k * 2 + 1, l, (l + r) / 2, x);
			updateTree(a, b, k * 2 + 2, (l + r + 1) / 2, r, x);
			node[k] = uniteData(node[k * 2 + 1], len, node[k * 2 + 2], len);
		}
	}
	LL getValue(int a, int b, int k, int l, int r) {
		if (a <= l && r <= b) {
			return node[k];
		}
		else if (a <= r && l <= b) {
			int len = (r - l + 1) / 2;
			for (int i = 0; i<2; ++i) {
				node[k * 2 + 1 + i] = updateData(node[k * 2 + 1 + i], len, delay[k]);
				delay[k * 2 + 1 + i] = updateDelay(delay[k * 2 + 1 + i], delay[k]);
			}
			delay[k] = INIT_DELAY;
			int len1 = max(0, min(b, (l + r) / 2) - max(a, l) + 1);
			int len2 = max(0, min(b, r) - max(a, (l + r + 1) / 2) + 1);
			LL v1 = getValue(a, b, k * 2 + 1, l, (l + r) / 2);
			LL v2 = getValue(a, b, k * 2 + 2, (l + r + 1) / 2, r);
			return uniteData(v1, len1, v2, len2);
		}
		else {
			return INIT_DATA;
		}
	}
public:
	SegmentTree(int n0) {
		n = 1;
		while (n < n0)
			n *= 2;
		node.assign(2 * n - 1, INIT_DATA);
		delay.assign(2 * n - 1, INIT_DELAY);
		pPow.resize(n + 1, 1);
		pPowSum.resize(n + 1, 0);
		for (int i = 1; i <= n; ++i) {
			pPow[i] = pPow[i - 1] * P % MOD;
			pPowSum[i] = pPowSum[i - 1] + pPow[i - 1];
		}
	}
	void update(int a, int b, char x) {
		updateTree(a, b, 0, 0, n - 1, x);
	}
	LL get(int a, int b) {
		return getValue(a, b, 0, 0, n - 1);
	}
};
SegmentTree seg(210000);
int main() {
	cin >> n;
	cin >> str;
	cin >> q;
	rep(i, str.size())seg.update(i, i, str[i]);
	rep(i, q) {
		LL x, y, z, w;
		char c;
		cin >> ss;
		if (ss == "comp") {
			cin >> x >> y >> z >> w;
			x--, y--, z--, w--;
			LL size = min(y - x, w - z);
			LL l = -1, r = size + 1, mid;
			while (r-l>1) {
				mid = (l + r) / 2;
				LL s1 = seg.get(x, x + mid);
				LL s2 = seg.get(z, z + mid);
				if (s1 == s2)
					l = mid;
				else
					r = mid;
			}
			if (l == size) {
				if (y - x == w - z)
					cout << "e" << endl;
				else if (y - x < w - z)
					cout << "s" << endl;
				else
					cout<<"t"<<endl;
			}
			else {
				if (seg.get(x + l+1, x + l+1) < seg.get(z + l+1, z + l+1)) 
					cout << "s" << endl;
				else
					cout << "t" << endl;
			}
		}
		else {
			cin >> x >> y >> c;
			x--, y--;
			seg.update(x, y, c);
		}
	}
	return 0;
}
accuracy: 1
time_ms: 1670
memory_kb: 12048
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}


template <typename T,typename E>
struct SegmentTree{
  using F = function<T(T,T)>;
  using G = function<T(T,E)>;
  using H = function<E(E,E)>;
  int n,height;
  F f;
  G g;
  H h;
  T ti;
  E ei;
  vector<T> dat;
  vector<E> laz;
  SegmentTree(F f,G g,H h,T ti,E ei):
    f(f),g(g),h(h),ti(ti),ei(ei){}
  
  void init(int n_){
    n=1;height=0;
    while(n<n_) n<<=1,height++;
    dat.assign(2*n,ti);
    laz.assign(2*n,ei);
  }
  void build(const vector<T> &v){
    int n_=v.size();
    init(n_);
    for(int i=0;i<n_;i++) dat[n+i]=v[i];
    for(int i=n-1;i;i--)
      dat[i]=f(dat[(i<<1)|0],dat[(i<<1)|1]);
  }
  inline T reflect(int k){
    return laz[k]==ei?dat[k]:g(dat[k],laz[k]);
  }
  inline void eval(int k){
    if(laz[k]==ei) return;
    laz[(k<<1)|0]=h(laz[(k<<1)|0],laz[k]);
    laz[(k<<1)|1]=h(laz[(k<<1)|1],laz[k]);
    dat[k]=reflect(k);
    laz[k]=ei;
  }
  inline void thrust(int k){
    for(int i=height;i;i--) eval(k>>i);
  }
  inline void recalc(int k){    
    while(k>>=1)
      dat[k]=f(reflect((k<<1)|0),reflect((k<<1)|1));
  }
  void update(int a,int b,E x){
    thrust(a+=n);
    thrust(b+=n-1);
    for(int l=a,r=b+1;l<r;l>>=1,r>>=1){
      if(l&1) laz[l]=h(laz[l],x),l++;
      if(r&1) --r,laz[r]=h(laz[r],x);
    }
    recalc(a);
    recalc(b);
  }
  void set_val(int a,T x){
    thrust(a+=n);
    dat[a]=x;laz[a]=ei;
    recalc(a);
  }
  T query(int a,int b){
    thrust(a+=n);
    thrust(b+=n-1);
    T vl=ti,vr=ti;
    for(int l=a,r=b+1;l<r;l>>=1,r>>=1) {
      if(l&1) vl=f(vl,reflect(l++));
      if(r&1) vr=f(reflect(--r),vr);
    }
    return f(vl,vr);
  }
};


struct FastIO{
  FastIO(){
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
}fastio_beet;

//INSERT ABOVE HERE
signed main(){
  int n;
  cin>>n;
  string s;
  cin>>s;
  int q;
  cin>>q;

  using ull = unsigned long long;
  using P = pair<ull, int>;
  
  const ull B = 1777771;
  vector<ull> po(n<<1,1);
  for(int i=1;i<(int)po.size();i++) po[i]=po[i-1]*B;

  vector<vector<ull> > hs(26,vector<ull>(n<<1,0));
  for(int c=0;c<26;c++){
    for(int i=1;i<(int)hs[c].size();i++)
      hs[c][i]=hs[c][i-1]*B+(c+'a');
  }
  auto f=[&](P a,P b){
           return P(a.first*po[b.second]+b.first,a.second+b.second);};
  auto g=[&](P a,int b){
           return P(hs[b][a.second],a.second);};
  auto h=[&](int a,int b){a++;return b;};

  P ti(0,0);
  int ei=-1;
  SegmentTree<P, int> seg(f,g,h,ti,ei);
  
  vector<P> vp;
  for(int i=0;i<n;i++) vp.emplace_back((ull)s[i],1);
  seg.build(vp);
  
  for(int i=0;i<q;i++){
    string type;
    cin>>type;
    if(type=="set"){
      int x,y;
      char z;
      cin>>x>>y>>z;
      x--;
      seg.update(x,y,z-'a');
    }
    if(type=="comp"){
      int a,b,c,d;
      cin>>a>>b>>c>>d;
      a--;c--;
      if(seg.query(a,b)==seg.query(c,d)){
        cout<<"e\n";
        continue;
      }
      int w=min(b-a,d-c);
      //cout<<seg.query(a,a+w).first<<" "<<seg.query(a,a+w).second<<endl;
      //cout<<seg.query(c,c+w).first<<" "<<seg.query(c,c+w).second<<endl;
      if(seg.query(a,a+w)==seg.query(c,c+w)){
        cout<<(b-a<d-c?"s":"t")<<"\n";
        continue;
      }
      int l=0,r=w;
      while(l+1<r){
        int m=(l+r)>>1;
        if(seg.query(a,a+m)==seg.query(c,c+m)) l=m;
        else r=m;
      }
      //cout<<a<<" "<<c<<":"<<l<<" "<<r<<endl;
      cout<<(seg.query(a+l,a+r)<seg.query(c+l,c+r)?"s":"t")<<"\n";
    }
  }
  cout<<flush;
  return 0;
}
accuracy: 1
time_ms: 360
memory_kb: 103112
final_rank: 15

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

#define MAX 200012

__int128 MOD=1007ll;
__int128 MOD2=1000000000000000031LL;

struct st{
	int sz;
	__int128 val;
	char lazy;
	st(){
		val=0;
		sz=0;
		lazy=0;
	}
};
st seg[MAX*4];
char buf[MAX];
string s;
int n;
__int128 pw[MAX];
__int128 im[MAX];

st merge(st a,st b){
	st r;
	r.sz=a.sz+b.sz;
	r.val=a.val+b.val*pw[a.sz];
	if(r.val>=MOD2)r.val%=MOD2;
	return r;
}

inline void init(int b,int l,int r){
	if(l+1==r){
		seg[b].val=s[l];
		seg[b].sz=1;
		return;
	}
	init(b*2+1,l,(l+r)>>1);
	init(b*2+2,(l+r)>>1,r);
	seg[b]=merge(seg[b*2+1],seg[b*2+2]);
}
inline void update(int b){
	if(seg[b].lazy==0)return;
	seg[b].val=im[seg[b].sz-1]*seg[b].lazy;
	if(seg[b].val>=MOD2)seg[b].val%=MOD2;
	if(b*2+2<MAX*4){
		seg[b*2+1].lazy=seg[b].lazy;
		seg[b*2+2].lazy=seg[b].lazy;
	}
	seg[b].lazy=0;
}
inline void add(int b,int l,int r,int ll,int rr,char z){
	update(b);
	if(r<=ll||rr<=l)return;
	if(ll<=l&&r<=rr){
		seg[b].lazy=z;
		update(b);
		return;
	}
	add(b*2+1,l,(l+r)>>1,ll,rr,z);
	add(b*2+2,(l+r)>>1,r,ll,rr,z);
	seg[b]=merge(seg[b*2+1],seg[b*2+2]);
}
inline st get_val(int b,int l,int r,int ll,int rr){
	update(b);
	if(r<=ll||rr<=l)return st();
	if(ll<=l&&r<=rr){
		return seg[b];
	}
	return merge(get_val(b*2+1,l,(l+r)>>1,ll,rr),get_val(b*2+2,(l+r)>>1,r,ll,rr));
}

int q;
long long int hs(int a,int b){
	return get_val(0,0,n,a,b+1).val;
}
int main(){
	pw[0]=1;
	for(int i=1;i<MAX;i++){
		pw[i]=pw[i-1];
		pw[i]*=MOD;
		pw[i]%=MOD2;
	}
	im[0]=pw[0];
	for(int i=1;i<MAX;i++){
		im[i]=im[i-1]+pw[i];
		if(im[i]>=MOD2)im[i]%=MOD2;
	}
	cin>>n;
	scanf("%s",buf);
	s=buf;
	init(0,0,n);
	cin>>q;
	while(q--){
		scanf("%s",buf);
		if(buf[0]=='s'){
			int l,r;
			scanf("%d%d",&l,&r);
			scanf("%s",buf);
			l--;
			add(0,0,n,l,r,buf[0]);
			continue;
		}	
		//cout<<"end"<<endl;
		int a,b;
		scanf("%d%d",&a,&b);
		int c,d;
		scanf("%d%d",&c,&d);
		a--;
		b--;
		c--;
		d--;
		char f='s';
		char s='t';
		if((b-a)>(d-c)){
			swap(a,c);
			swap(b,d);
			swap(f,s);
		}
		int len=b-a;
		if(hs(a,b)==hs(c,c+len)){
			if((b-a)!=(d-c)){
				buf[0]=f;
				buf[1]='\0';
				printf("%s",buf);
				puts("");
			}
			else{
				puts("e");
			}
			continue;
		}
		int mint=0;
		int maxt=len;
		while(mint+1<maxt){
			int mid=(mint+maxt)/2;
			if(hs(a,a+mid)==hs(c,c+mid)){
				mint=mid+1;
			}
			else{
				maxt=mid;
			}
		}
		if(hs(a,a+mint)==hs(c,c+mint)){
			mint++;
		}
		int mid=mint;
		if(hs(a+mid,a+mid)<hs(c+mid,c+mid)){
			buf[0]=f;
			buf[1]='\0';
			printf("%s",buf);
			puts("");
		}
		else{
			buf[0]=s;
			buf[1]='\0';
			printf("%s",buf);
			puts("");
		}
		//cout<<"end"<<endl;
	}
	return 0;
}
accuracy: 1
time_ms: 1150
memory_kb: 47356
final_rank: 13

code_snippet: #include<deque>
#include<queue>
#include<vector>
#include<algorithm>
#include<iostream>
#include<set>
#include<cmath>
#include<tuple>
#include<string>
//#include<chrono>
#include<functional>
#include<iterator>
#include<random>
#include<list>
#include<unordered_set>
#include<array>
#include<map>
#include<iomanip>
#include<assert.h>
#include<bitset>
#include<stack>
using namespace std;
typedef long long int llint;
typedef long double lldo;
#define mp make_pair
#define mt make_tuple
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define fir first
#define sec second
#define res resize
#define ins insert
#define era erase
/*
cout<<setprecision(20);
cin.tie(0);
ios::sync_with_stdio(false);
*/
const llint moA=1e9+7;
const llint moB=1e9+9;
const llint big=2.19e15+1;
const long double pai=3.141592653589793238462643383279502884197;
const long double eps=1e-15;
template <class T,class U>void mineq(T& a,U b){if(a>b){a=b;}}
template <class T,class U>void maxeq(T& a,U b){if(a<b){a=b;}}
llint gcd(llint a,llint b){if(a%b==0){return b;}else return gcd(b,a%b);}
//llint lcm(llint a,llint b){return a/gcd(a,b)*b;}
template<class T> void SO(T& ve){sort(ve.begin(),ve.end());}
template<class T> void REV(T& ve){reverse(ve.begin(),ve.end());}
template<class T>llint LBI(vector<T>&ar,T in){return lower_bound(ar.begin(),ar.end(),in)-ar.begin();}
template<class T>llint UBI(vector<T>&ar,T in){return upper_bound(ar.begin(),ar.end(),in)-ar.begin();}
//segtree
//hash値,埋めるの発生有無
//長さは262144固定?
//Qlog^2 n
llint zyA[20],zyB[20];
llint ruA[20],ruB[20];
class rori{public:
	llint hA,hB;
	char ume;
	rori(void){hA=0;hB=0;ume=0;}
	rori(int inA,int inB,int dumy){hA=inA,hB=inB,ume=0;}
	rori(int in,int naga){ume=in;hA=(in*ruA[naga])%moA,hB=(in*ruB[naga])%moB;}
};
rori tna(rori me,rori us,int an){return rori((me.hA*zyA[an]+us.hA)%moA,(me.hB*zyB[an]+us.hB)%moB,0);}
rori tna(rori me,rori us,llint Ak,llint Bk){return rori((me.hA*Ak+us.hA)%moA,(me.hB*Bk+us.hB)%moB,0);}

void bun(rori&per,rori&kox,rori&koy,int an){//anは子のを入れる
	if(per.ume){kox=rori(per.ume,an);koy=kox;per.ume=0;}
}
int main(void){
	llint n,h,i,j;cin>>n;
	zyA[0]=101,zyB[0]=101;
	for(i=1;i<20;i++){zyA[i]=(zyA[i-1]*zyA[i-1])%moA;zyB[i]=(zyB[i-1]*zyB[i-1])%moB;}
	for(i=0;i<20;i++){
		llint AA=1,BB=1;
		ruA[i]=0;ruB[i]=0;
		for(j=0;j<(1<<i);j++){ruB[i]+=BB;ruB[i]%=moB;BB*=101;BB%=moB;ruA[i]+=AA;ruA[i]%=moA;AA*=101;AA%=moA;}
	}
	string U;cin>>U;
	static rori seg[524288];
	for(i=1;i<=n;i++){seg[i+262144]=rori(U[i-1],0);}
	for(i=n+1;i<262144;i++){seg[i+262144]=rori(0,0);}
	h=0;
	for(i=262143;i>0;i--){
		seg[i]=tna(seg[i+i],seg[i+i+1],h);
		if((i&(-i))==i){h++;}
	}
	int Q;cin>>Q;
	while(Q--){
		string mei;cin>>mei;
		if(mei=="set"){
			int x,y;char z;cin>>x>>y>>z;
			x+=262143;y+=262145;int xx=x,yy=y;
			for(h=18;h>0;h--){int G=x>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=y>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
			h=0;
			while(x+1<y){
				if(x%2==0){seg[x+1]=rori(z,h);}
				if(y%2==1){seg[y-1]=rori(z,h);}
				x>>=1;y>>=1;h++;
			}
			x=xx;y=yy;
			h=0;
			while(x>1){
				x>>=1;y>>=1;
				seg[x]=tna(seg[x<<1],seg[(x<<1)+1],h);
				seg[y]=tna(seg[y<<1],seg[(y<<1)+1],h);
				h++;
			}
		}else{
			int a,b,c,d;cin>>a>>b>>c>>d;
			int bmin=0,bmax=min(b-a+1,d-c+1)+1;
			//普通に二分探索! セグ木上の二分探索テクは使用不可
			//最大何文字が一致しているか調べる
			//bmin文字一致している
			int Sa=a+262143,Ta=c+262143;
			for(h=18;h>0;h--){int G=Sa>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=Ta>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
			while(bmax-bmin>1){
				
				Sa=a+262143;Ta=c+262143;
				int bgen=(bmin+bmax)>>1;
				int Se=a+bgen+262144,Te=c+bgen+262144;
				for(h=18;h>0;h--){int G=Se>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=Te>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
				rori Sans,Tans;
				{
					llint Auk=1,Buk=1;
					rori nowM,nowU;
					int h=0;
					while(Sa+1<Se){
						if(Sa%2==0){nowM=tna(nowM,seg[Sa+1],h);}
						if(Se%2==1){nowU=tna(seg[Se-1],nowU,Auk,Buk);Auk*=zyA[h];Auk%=moA;Buk*=zyB[h];Buk%=moB;}
						Sa>>=1;Se>>=1;h++;
					}
					//cerr<<"M,U="<<nowM.hA<<" "<<nowU.hA<<" ";
					Sans=tna(nowM,nowU,Auk,Buk);
					//cerr<<Sans.hA<<endl;
				}
				{
					llint Auk=1,Buk=1;
					rori nowM,nowU;
					int h=0;
					while(Ta+1<Te){
						if(Ta%2==0){nowM=tna(nowM,seg[Ta+1],h);}
						if(Te%2==1){nowU=tna(seg[Te-1],nowU,Auk,Buk);Auk*=zyA[h];Auk%=moA;Buk*=zyB[h];Buk%=moB;}
						Ta>>=1;Te>>=1;h++;
					}
					//cerr<<"M,U="<<nowM.hA<<" "<<nowU.hA<<" ";
					Tans=tna(nowM,nowU,Auk,Buk);
					//cerr<<Tans.hA<<endl;
				}
				if(Sans.hA==Tans.hA&&Sans.hB==Tans.hB){bmin=bgen;}else{bmax=bgen;}
				//cerr<<"bmin="<<bmin<<endl;
			}
			char kota;
			int aaa=262144+a+bmin,ccc=262144+c+bmin;
			for(h=18;h>0;h--){int G=aaa>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=ccc>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
			//cerr<<seg[262144+a+bmin].ume<<endl;
			if(b-a+1==d-c+1&&d-c+1==bmin){kota='e';}
			else if(b-a+1>d-c+1&&d-c+1==bmin){kota='t';}
			else if(b-a+1<d-c+1&&b-a+1==bmin){kota='s';}
			else if(seg[262144+a+bmin].ume<seg[262144+c+bmin].ume){kota='s';}
			else{kota='t';}
			cout<<kota<<endl;
		}
	}
	return 0;
}
accuracy: 1
time_ms: 680
memory_kb: 15628
final_rank: 3

code_snippet: #include<deque>
#include<queue>
#include<vector>
#include<algorithm>
#include<iostream>
#include<set>
#include<cmath>
#include<tuple>
#include<string>
//#include<chrono>
#include<functional>
#include<iterator>
#include<random>
#include<list>
#include<unordered_set>
#include<array>
#include<map>
#include<iomanip>
#include<assert.h>
#include<bitset>
#include<stack>
using namespace std;
typedef long long int llint;
typedef long double lldo;
#define mp make_pair
#define mt make_tuple
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define fir first
#define sec second
#define res resize
#define ins insert
#define era erase
/*
cout<<setprecision(20);
cin.tie(0);
ios::sync_with_stdio(false);
*/
const llint moA=1e9+7;
const llint moB=1e9+9;
const llint big=2.19e15+1;
const long double pai=3.141592653589793238462643383279502884197;
const long double eps=1e-15;
template <class T,class U>void mineq(T& a,U b){if(a>b){a=b;}}
template <class T,class U>void maxeq(T& a,U b){if(a<b){a=b;}}
llint gcd(llint a,llint b){if(a%b==0){return b;}else return gcd(b,a%b);}
//llint lcm(llint a,llint b){return a/gcd(a,b)*b;}
template<class T> void SO(T& ve){sort(ve.begin(),ve.end());}
template<class T> void REV(T& ve){reverse(ve.begin(),ve.end());}
template<class T>llint LBI(vector<T>&ar,T in){return lower_bound(ar.begin(),ar.end(),in)-ar.begin();}
template<class T>llint UBI(vector<T>&ar,T in){return upper_bound(ar.begin(),ar.end(),in)-ar.begin();}
//segtree
//hash値,埋めるの発生有無
//長さは262144固定?
//Qlog^2 n
llint zyA[20],zyB[20];
llint ruA[20],ruB[20];
class rori{public:
	llint hA,hB;
	char ume;
	rori(void){hA=0;hB=0;ume=0;}
	rori(int inA,int inB,int dumy){hA=inA,hB=inB,ume=0;}
	rori(int in,int naga){ume=in;hA=(in*ruA[naga])%moA,hB=(in*ruB[naga])%moB;}
};
rori tna(rori me,rori us,int an){return rori((me.hA*zyA[an]+us.hA)%moA,(me.hB*zyB[an]+us.hB)%moB,0);}
rori tna(rori me,rori us,llint Ak,llint Bk){return rori((me.hA*Ak+us.hA)%moA,(me.hB*Bk+us.hB)%moB,0);}

void bun(rori&per,rori&kox,rori&koy,int an){//anは子のを入れる
	if(per.ume){kox=rori(per.ume,an);koy=kox;per.ume=0;}
}
int main(void){
	llint n,h,i,j;cin>>n;
	zyA[0]=101,zyB[0]=101;
	for(i=1;i<20;i++){zyA[i]=(zyA[i-1]*zyA[i-1])%moA;zyB[i]=(zyB[i-1]*zyB[i-1])%moB;}
	for(i=0;i<20;i++){
		llint AA=1,BB=1;
		ruA[i]=0;ruB[i]=0;
		for(j=0;j<(1<<i);j++){ruB[i]+=BB;ruB[i]%=moB;BB*=101;BB%=moB;ruA[i]+=AA;ruA[i]%=moA;AA*=101;AA%=moA;}
	}
	string U;cin>>U;
	static rori seg[524288];
	for(i=1;i<=n;i++){seg[i+262144]=rori(U[i-1],0);}
	for(i=n+1;i<262144;i++){seg[i+262144]=rori(0,0);}
	h=1;
	for(i=262143;i>0;i--){
		seg[i]=tna(seg[i+i],seg[i+i+1],0);
		if((i&(-i))==i){h++;}
	}
	int Q;cin>>Q;
	while(Q--){
		string mei;cin>>mei;
		if(mei=="set"){
			int x,y;char z;cin>>x>>y>>z;
			x+=262143;y+=262145;int xx=x,yy=y;
			for(h=18;h>0;h--){int G=x>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=y>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
			h=0;
			while(x+1<y){
				if(x%2==0){seg[x+1]=rori(z,h);}
				if(y%2==1){seg[y-1]=rori(z,h);}
				x>>=1;y>>=1;h++;
			}
			x=xx;y=yy;
			h=0;
			while(x>1){
				x>>=1;y>>=1;
				seg[x]=tna(seg[x<<1],seg[(x<<1)+1],h);
				seg[y]=tna(seg[y<<1],seg[(y<<1)+1],h);
				h++;
			}
		}else{
			int a,b,c,d;cin>>a>>b>>c>>d;
			int bmin=0,bmax=min(b-a+1,d-c+1)+1;
			//普通に二分探索! セグ木上の二分探索テクは使用不可
			//最大何文字が一致しているか調べる
			//bmin文字一致している
			int Sa=a+262143,Ta=c+262143;
			for(h=18;h>0;h--){int G=Sa>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=Ta>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
			while(bmax-bmin>1){
				
				Sa=a+262143;Ta=c+262143;
				int bgen=(bmin+bmax)>>1;
				int Se=a+bgen+262144,Te=c+bgen+262144;
				for(h=18;h>0;h--){int G=Se>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=Te>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
				rori Sans,Tans;
				{
					llint Auk=1,Buk=1;
					rori nowM,nowU;
					int h=0;
					while(Sa+1<Se){
						if(Sa%2==0){nowM=tna(nowM,seg[Sa+1],h);}
						if(Se%2==1){nowU=tna(seg[Se-1],nowU,Auk,Buk);Auk*=zyA[h];Auk%=moA;Buk*=zyB[h];Buk%=moB;}
						Sa>>=1;Se>>=1;h++;
					}
					//cerr<<"M,U="<<nowM.hA<<" "<<nowU.hA<<" ";
					Sans=tna(nowM,nowU,Auk,Buk);
					//cerr<<Sans.hA<<endl;
				}
				{
					llint Auk=1,Buk=1;
					rori nowM,nowU;
					int h=0;
					while(Ta+1<Te){
						if(Ta%2==0){nowM=tna(nowM,seg[Ta+1],h);}
						if(Te%2==1){nowU=tna(seg[Te-1],nowU,Auk,Buk);Auk*=zyA[h];Auk%=moA;Buk*=zyB[h];Buk%=moB;}
						Ta>>=1;Te>>=1;h++;
					}
					//cerr<<"M,U="<<nowM.hA<<" "<<nowU.hA<<" ";
					Tans=tna(nowM,nowU,Auk,Buk);
					//cerr<<Tans.hA<<endl;
				}
				if(Sans.hA==Tans.hA&&Sans.hB==Tans.hB){bmin=bgen;}else{bmax=bgen;}
				//cerr<<"bmin="<<bmin<<endl;
			}
			char kota;
			int aaa=262144+a+bmin,ccc=262144+c+bmin;
			for(h=18;h>0;h--){int G=aaa>>h;bun(seg[G],seg[(G<<1)],seg[(G<<1)+1],h-1);int F=ccc>>h;bun(seg[F],seg[(F<<1)],seg[(F<<1)+1],h-1);}
			//cerr<<seg[262144+a+bmin].ume<<endl;
			if(b-a+1==d-c+1&&d-c+1==bmin){kota='e';}
			else if(b-a+1>d-c+1&&d-c+1==bmin){kota='t';}
			else if(b-a+1<d-c+1&&b-a+1==bmin){kota='s';}
			else if(seg[262144+a+bmin].ume<seg[262144+c+bmin].ume){kota='s';}
			else{kota='t';}
			cout<<kota<<endl;
		}
	}
	return 0;
}
accuracy: 0.5555555555555556
time_ms: 500
memory_kb: 15652
final_rank: 20