Problem:
Tree Keyboard
Dr. PCK has developed a revolutionary input device for computers, the Tree Keyboard. This keyboard has $N$ keys, each numbered $1$ to $N$, and they are wired with $N-1$ wires. You can also reach other keys by following some wires from any key.
As a keyboard setting, each key can be assigned a single character, and these can be changed at will at any time. On this keyboard, when you press a key $s$ and then a key $t$, the characters of all the keys on the shortest path from key $s$ to key $t$ are entered once in sequence. At this time, if key $s$ and key $t$ are the same key, the character will be entered only once.
Given the information about the Tree Keyboard, the keyboard setup operation, and the input operation, create a program to find the number of different character strings that have been input to the computer up to the point immediately after each input operation.
Input
The input is given in the following format.
$N$
$k_1$
$k_2$
:
$k_N$
$u_1$ $v_1$
$u_2$ $v_2$
:
$u_{N-1}$ $v_{N-1}$
$Q$
$q_1$
$q_2$
:
$q_Q$
In the first line, the number of keys $N$ ($1 \leq N \leq 100,000$) is given. In the following $N$ line, the first lowercase letter $k_i$ set for key $i$ is given. In the following $N-1$ line, the wire information is given. The $u_i$ and $v_i$ ($1 \leq u_i < v_i \leq N$) are the numbers of the keys that the $i$th wire connects to.
In the following line, the number of operations $Q$ ($1 \leq Q \leq 100,000$) is given. In the succeeding $Q$ lines, each operation is given in the following form.
1 $s$ $t$
or
2 $k$ $c$
1
$s$ $t$ represents the input operation of hitting key $s$ ($1 \leq s \leq N$) and key $t$ ($1 \leq t \leq N$) in sequence.
2
$k$ $c$ represents the setting operation to change the printed character of key $k$ ($1 \leq k \leq N$) to lowercase English $c$.
Output
After each input operation, output the number of different strings that have been entered into the computer up to that point, each on a single line.
Sample Input and Output
Sample Input
6
a
b
a
c
e
b
1 2
2 3
2 4
2 5
5 6
4
1 1 6
1 3 4
2 5 c
1 1 5
Sample Output
1
2
2

Submitted solutions:

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

template<typename S, S(*op)(S, S), S(*e)()>
struct segtree {
	int siz;
	vector<S> node;
	
	segtree() = default;
	segtree(int n){
		siz = 1;
		while(siz < n) siz *= 2;
		node.assign(siz*2, e());
	}
	segtree(const vector<S> &v) : segtree(v.size()) {
		for(int i = 0; i < (int)v.size(); i++) node[i+siz] = v[i];
		for(int i = siz-1; i >= 1; i--) node[i] = op(node[i*2], node[i*2+1]);
	}
	
	void set(int p, S x){
		p += siz;
		node[p] = x;
		while(p >>= 1) node[p] = op(node[p*2], node[p*2+1]);
	}
	
	S get(int p) {return node[p+siz];}
	
	S prod(int l, int r){
		l += siz; r += siz;
		S L = e(), R = e();
		while(l < r){
			if(l&1) L = op(L, node[l++]);
			if(r&1) R = op(node[--r], R);
			l >>= 1; r >>= 1;
		}
		return op(L, R);
	}
};

template<typename S, S(*op)(S, S), S(*e)()>
struct HLD {
    using seg_t = segtree<S, op, e>;
    
    int n;
    vector<int> dep, in, out, head, par;
    vector<array<int, 2>> E;
    seg_t node[2];
    
    HLD(int _n) : n(_n), dep(n), in(n), out(n), head(n), par(n) {}
    
    void add_edge(int u, int v){
        E.push_back({u, v});
    }
    
    void build(const vector<S> &v){
        vector<vector<int>> G(n);
        for(auto i : E){
            G[i[0]].push_back(i[1]);
            G[i[1]].push_back(i[0]);
        }
        
        vector<int> sub_siz(n, 1);
        
        par[0] = -1;
        auto f1 = [&](auto self, int pos)->void {
            for(auto nex : G[pos]){
                if(nex == par[pos]) continue;
                par[nex] = pos;
                self(self, nex);
                sub_siz[pos] += sub_siz[nex];
            }
        }; f1(f1, 0);
        
        int cnt = 0;
        auto f2 = [&](auto self, int pos)->void {
            in[pos] = cnt++;
            if(sub_siz[pos] == 1){
                out[pos] = cnt;
                return;
            }
            int hv = -1;
            for(auto nex : G[pos]){
                if(nex == par[pos]) continue;
                if(hv == -1 || sub_siz[hv] < sub_siz[nex]) hv = nex;
            }
            dep[hv] = dep[pos];
            head[hv] = head[pos];
            self(self, hv);
            for(auto nex : G[pos]){
                if(nex == par[pos] || nex == hv) continue;
                dep[nex] = dep[pos]+1;
                head[nex] = nex;
                self(self, nex);
            }
            out[pos] = cnt;
        }; f2(f2, 0);
        
        vector<S> node_v(n);
        for(int i = 0; i < n; i++) node_v[in[i]] = v[i];
        
        node[0] = seg_t(node_v);
        reverse(node_v.begin(), node_v.end());
        node[1] = seg_t(node_v);
    }
    
    S prod(int l, int r){
        S l_ans = e(), r_ans = e();
        while(true){
            if(dep[l] >= dep[r] && head[l] != head[r]){
                l_ans = op(l_ans, node[1].prod(n-in[l]-1, n-in[head[l]]));
                l = par[head[l]];
            } else if(dep[l] < dep[r]){
                r_ans = op(node[0].prod(in[head[r]], in[r]+1), r_ans);
                r = par[head[r]];
            } else {
                if(in[l] <= in[r]) l_ans = op(l_ans, node[0].prod(in[l], in[r]+1));
                else r_ans = op(node[1].prod(n-in[l]-1, n-in[r]), r_ans);
                return op(l_ans, r_ans);
            }
        }
    }
    
    S prod(int r){
        return node[0].prod(in[r], out[r]);
    }
    
    S get(int p){
        return node[0].get(in[p]);
    }
    
    void set(int p, const S &x){
        node[0].set(in[p], x);
        node[1].set(n-in[p]-1, x);
    }
    
};

/*
    ー使い方ー
    S, op, eを決める
    コンストラクタ HLD hoge(int n) で頂点数指定
    add_edge(int u, int v) で無向辺追加
    その後，頂点の値vectorを引数に build(vector<S> v) を呼び出す
    
    set(int p, S x) で，頂点pの値をxに変える
    prod(int l, int r) でlからrのパスクエリ
    prod(int r) で部分木クエリ
*/

using ll = long long;
constexpr ll mod = (1ll<<61) - 1;
ll mu(ll a, ll b) {
	__int128_t c = (__int128_t)a * b;
	c = (c&mod) + (c>>61);
	return c < mod ? c : c-mod;
}

struct S{
	ll val;
	ll po;
};
S op(S l, S r){
	ll val = mu(l.val, r.po) + r.val;
	val = val < mod ? val : val - mod;
	ll po = mu(l.po, r.po);
	return {val, po};
}
S e(){return {0, 1};}

int main(){
	int n;
	cin >> n;
	vector<S> A(n);
	for(int i = 0; i < n; i++){
		char c;
		cin >> c;
		A[i].val = c - 'a' + 1;
		A[i].po = 37;
	}
	
	HLD<S, op, e> seg(n);
	for(int i = 0; i < n-1; i++){
		int u, v;
		cin >> u >> v;
		u--; v--;
		seg.add_edge(u, v);
	}
	seg.build(A);
	
	int q;
	cin >> q;
	
	unordered_set<ll> S;
	while(q--){
		int t;
		cin >> t;
		if(t == 1){
			int s, t;
			cin >> s >> t;
			s--; t--;
			S.insert(seg.prod(s, t).val);
			cout << S.size() << '\n';
		} else {
			int k; char c;
			cin >> k >> c;
			k--;
			c -= 'a' - 1;
			seg.set(k, {c, 37});
		}
	}
}
accuracy: 1
time_ms: 170
memory_kb: 32540
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define int long long
int bs = 27;
int mod1 = 998244853, mod2 = 968244353;
int I1[100100], I2[100100], I1_[100100], I2_[100100];
int pw(int a, int p, int mod){
    int ans = 1, now = a;
    while(p){
        if(p&1) ans *= now;
        ans %= mod;
        now *= now;
        now %= mod;
        p >>= 1;
    }
    return ans;
}
int v(int a, int mod){
    return pw(a,mod-2,mod);
}
struct SEG{
private:
    int n;
    vector<int> node;
public:
    SEG(int N){
        n = 1;
        while(n < N) n *= 2;
        node.resize(2*n);
    }
    void update(int i, int x){
        i += n;
        node[i] = x;
        while(i > 1){
            i >>= 1;
            node[i] = node[2*i]+node[2*i+1];
        }
        return;
    }
    int f(int a, int b){
        int l = a+n, r = b+n;
        int ans = 0;
        while(l != r){
            if(l&1) ans += node[l++];
            if(r&1) ans += node[--r];
            l >>= 1;
            r >>= 1;
        }
        return ans;
    }
};
signed main(){
    int N;
    cin>>N;
    for(int i=0;i<=N;i++){
        I1[i] = pw(bs,i,mod1);
        I2[i] = pw(bs,i,mod2);
        I1_[i] = v(I1[i],mod1);
        I2_[i] = v(I2[i],mod2);
    }
    vector<int> A(N);
    for(int i=0;i<N;i++){
        char c;
        cin>>c;
        A[i] = c-'a'+1;
    }
    vector<vector<int>> G(N);
    for(int i=0;i<N-1;i++){
        int a,b;
        cin>>a>>b;
        a--;
        b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    vector<int> dist(N,1e18);
    {
        queue<int> q;
        q.push(0);
        dist[0] = 0;
        while(!q.empty()){
            int pos = q.front();
            q.pop();
            for(int x:G[pos])if(dist[x] > N){
                dist[x] = dist[pos] + 1;
                q.push(x);
            }
        }
    }
    int n = 18;
    vector<vector<int>> P(n,vector<int>(N,-1));
    for(int i=0;i<N;i++)for(int x:G[i])if(dist[x] < dist[i]) P[0][i] = x;
    for(int i=1;i<n;i++)for(int j=0;j<N;j++)if(P[i-1][j] != -1) P[i][j] = P[i-1][P[i-1][j]];
    auto lca = [&](int a, int b) -> int {
        if(dist[a] > dist[b]) swap(a,b);
        int k = dist[b]-dist[a];
        for(int i=0;i<n;i++)if(k&(1<<i)) b = P[i][b];
        if(a == b) return a;
        for(int i=n-1;i>=0;i--)if(P[i][a] != P[i][b]){
            a = P[i][a];
            b = P[i][b];
        }
        return P[0][a];
    };
    vector<int> in(N), out(N);
    {
        int now = 0;
        auto init_o = [&](auto &&self, int a, int p) -> void {
            in[a] = now;
            now++;
            for(auto x:G[a])if(x != p){
                self(self,x,a);
            }
            out[a] = now;
            now++;
            return;
        };
        init_o(init_o,0,-1);
    }
    SEG u1(2*N), u2(2*N), d1(2*N), d2(2*N);
    auto ud = [&](int p, int x) -> void {
        A[p] = x;
        u1.update(in[p],((A[p]*I1[N-1-dist[p]])%mod1));
        u1.update(out[p],-((A[p]*I1[N-1-dist[p]])%mod1));
        u2.update(in[p],((A[p]*I2[N-1-dist[p]])%mod2));
        u2.update(out[p],-((A[p]*I2[N-1-dist[p]])%mod2));
        d1.update(in[p],((A[p]*I1[dist[p]])%mod1));
        d1.update(out[p],-((A[p]*I1[dist[p]])%mod1));
        d2.update(in[p],((A[p]*I2[dist[p]])%mod2));
        d2.update(out[p],-((A[p]*I2[dist[p]])%mod2));
        return;
    };
    for(int i=0;i<N;i++) ud(i,A[i]);
    auto hs = [&](int s, int t) -> pair<int,int> {
        pair<int,int> ans;
        int l = lca(s,t);
        int d = dist[s]-dist[l];
        int a,b;
        a = u1.f(in[l],out[s]);
        a -= (A[l]*I1[N-1-dist[l]])%mod1;
        a %= mod1;
        a *= I1_[N-1-dist[s]];
        a %= mod1;
        b = d1.f(in[l],out[t]);
        b %= mod1;
        b *= I1_[dist[l]];
        b %= mod1;
        ans.first = (a+b*I1[d])%mod1+mod1;
        ans.first %= mod1;

        a = u2.f(in[l],out[s]);
        a -= (A[l]*I2[N-1-dist[l]])%mod2;
        a %= mod2;
        a *= I2_[N-1-dist[s]];
        a %= mod2;
        b = d2.f(in[l],out[t]);
        b %= mod2;
        b *= I2_[dist[l]];
        b %= mod2;
        ans.second = (a+b*I2[d])%mod2+mod2;
        ans.second %= mod2;

        return ans;
    };
    int Q;
    cin>>Q;
    set<pair<int,int>> st;
    while(Q--){
        int ty;
        cin>>ty;
        if(ty == 1){
            int a,b;
            cin>>a>>b;
            a--;
            b--;
            st.insert(hs(a,b));
            cout<<st.size()<<endl;
        }
        else{
            int k;
            char c;
            cin>>k>>c;
            int v = c-'a'+1;
            ud(k-1,v);
        }
    }
}
accuracy: 1
time_ms: 390
memory_kb: 57860
final_rank: 16

code_snippet: #define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/0478"



#include <cstdint>
#include <cstddef>

namespace zawa {

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

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

using usize = std::size_t;

} // namespace zawa

#include <iostream>
#include <iomanip>

namespace zawa {

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

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

} // namespace zawa



namespace zawa {

// @reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
class Mersenne61ModInt {
public:
    using Value = u64;
private:
    static constexpr Value MOD{(1ull << 61) - 1}; // == MASK61
    static constexpr Value MASK30{(1ull << 30) - 1};
    static constexpr Value MASK31{(1ull << 31) - 1};
    Value v_{};
public:
    constexpr Mersenne61ModInt() {}

    static constexpr Value Mod() noexcept {
        return MOD;
    }
    static constexpr Value Modulo(const Value& v) noexcept {
        Value res{(v >> 61) + (v & MOD)};
        res = (res >= MOD ? res - MOD : res);
        return res;
    }
    static constexpr Value UnsafeMul(const Value& a, const Value& b) noexcept {
        Value fa{a >> 31}, fb{b >> 31};
        Value ba{a & MASK31}, bb{b & MASK31};
        Value mid{fa * bb + fb * ba};
        return Value{2}*fa*fb + (mid >> 30) + ((mid & MASK30) << 31) + ba*bb;
    }
    static constexpr Value Mul(const Value& a, const Value& b) noexcept {
        return Modulo(UnsafeMul(a, b));
    }
};

};

#include <random>
#include <type_traits>

namespace zawa {

struct RollingHashMonoidData {
    using Value = Mersenne61ModInt::Value;
    using Size = usize;
    static Value base;
    Value hash{}, pow{1};
    usize len{};

    constexpr RollingHashMonoidData() = default;
    constexpr RollingHashMonoidData(Value h, Value p, usize l) : hash{h}, pow{p}, len{l} {}
    template <class T>
    constexpr RollingHashMonoidData(const T& v) 
        : hash{static_cast<Value>(v)}, pow{base}, len{1} {}
    // RollingHashMonoidData(const RollingHashMonoidData& data)
    //     : hash{data.hash}, pow{data.pow}, len{data.len} {}
    
    static Value randomValue(const Value& sigma) {
        return std::mt19937{std::random_device{}()}() % (Mersenne61ModInt::Mod() - sigma) + sigma + 1;
    }

    friend constexpr bool operator==(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash == rhs.hash and lhs.len == rhs.len;
    }
    friend constexpr bool operator!=(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash != rhs.hash or lhs.len != rhs.len;
    }
};

struct RollingHashMonoid {
    using Modulo = Mersenne61ModInt;
    using Element = RollingHashMonoidData;
    static constexpr Element identity() noexcept {
        return Element{};
    }
    static constexpr Element operation(const Element& lhs, const Element& rhs) noexcept {
        return Element{
            Modulo::Modulo(Modulo::UnsafeMul(lhs.hash, rhs.pow) + rhs.hash),
            Modulo::Mul(lhs.pow, rhs.pow),
            lhs.len + rhs.len
        };
    }
};

} // namespace zawa



#include <concepts>

namespace zawa {

namespace concepts {

template <class T>
concept Semigroup = requires {
    typename T::Element;
    { T::operation(std::declval<typename T::Element>(), std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

} // namespace concepts

} // namespace zawa


namespace zawa {

namespace concepts {

template <class T>
concept Identitiable = requires {
    typename T::Element;
    { T::identity() } -> std::same_as<typename T::Element>;
};

template <class T>
concept Monoid = Semigroup<T> and Identitiable<T>;

} // namespace

} // namespace zawa

namespace zawa {

template <concepts::Monoid M>
struct ReverseOrder {

    using Element = M::Element;
    
    static Element identity() {
        return M::identity();
    }

    static Element operation(const Element& L, const Element& R) {
        return M::operation(R, L);
    }

};

} // namespace zawa


#include <vector>
#include <cassert>
#include <functional>
#include <ostream>

namespace zawa {

template <concepts::Monoid Monoid>
class SegmentTree {
public:

    using VM = Monoid;

    using V = typename VM::Element;

    using OM = Monoid;

    using O = typename OM::Element;

    SegmentTree() = default;

    explicit SegmentTree(usize n) : m_n{ n }, m_dat(n << 1, VM::identity()) {}

    explicit SegmentTree(const std::vector<V>& dat) : m_n{ dat.size() }, m_dat(dat.size() << 1, VM::identity()) {
        for (usize i{} ; i < m_n ; i++) {
            m_dat[i + m_n] = dat[i];
        }
        for (usize i{m_n} ; i-- ; ) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    [[nodiscard]] inline usize size() const noexcept {
        return m_n;
    }

    [[nodiscard]] V get(usize i) const {
        assert(i < size());
        return m_dat[i + m_n];
    }

    [[nodiscard]] V operator[](usize i) const {
        assert(i < size());
        return m_dat[i + m_n];
    }

    void operation(usize i, const O& value) {
        assert(i < size());
        i += size();
        m_dat[i] = OM::operation(m_dat[i], value);
        while (i = parent(i), i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    void assign(usize i, const V& value) {
        assert(i < size());
        i += size();
        m_dat[i] = value;
        while (i = parent(i), i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    [[nodiscard]] V product(u32 l, u32 r) const {
        assert(l <= r and r <= size());
        V L{ VM::identity() }, R{ VM::identity() };
        for (l += size(), r += size() ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                L = VM::operation(L, m_dat[l++]);
            }
            if (r & 1) {
                R = VM::operation(m_dat[--r], R);
            }
        }
        return VM::operation(L, R);
    }

    template <class F>
    requires std::predicate<F, V>
    [[nodiscard]] usize maxRight(usize l, const F& f) {
        assert(l < size());
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(V)>>, "maxRight's argument f must be function bool(T)");
        assert(f(VM::identity()));
        usize res{l}, width{1};
        V prod{ VM::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += size() ; res + width <= size() ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(VM::operation(prod, m_dat[l]))) break; 
            res += width;
            prod = VM::operation(prod, m_dat[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= size() and f(VM::operation(prod, m_dat[l]))) {
                res += width;
                prod = VM::operation(prod, m_dat[l++]);
            } 
        }
        return res;
    }

    template <class F>
    requires std::predicate<F, V>
    [[nodiscard]] usize minLeft(usize r, const F& f) const {
        assert(r <= size());
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(V)>>, "minLeft's argument f must be function bool(T)");
        assert(f(VM::identity()));
        usize res{r}, width{1};
        V prod{ VM::identity() };
        for (r += size() ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(VM::operation(m_dat[r - 1], prod))) break;
            res -= width;
            prod = VM::operation(prod, m_dat[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(VM::operation(m_dat[r - 1], prod))) {
                res -= width;
                prod = VM::operation(m_dat[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (usize i{1} ; i < 2 * st.size() ; i++) {
            os << st.m_dat[i] << (i + 1 == 2 * st.size() ? "" : " ");
        }
        return os;
    }

private:

    constexpr u32 left(u32 v) const {
        return v << 1;
    }

    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }

    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize m_n;

    std::vector<V> m_dat;
};

} // namespace zawa


#include <algorithm>
#include <cmath>
#include <limits>
#include <utility>

namespace zawa {

template <class V>
class HeavyLightDecomposition {
public:

    static constexpr V Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(std::vector<std::vector<V>> T, V root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, V v, V p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (V x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, V v, V idx, V top) -> V {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(V v) const noexcept {
        assert(v < (V)size());
        return size_[v];
    }

    usize depth(V v) const noexcept {
        assert(v < (V)size());
        return dep_[v];
    }

    V parent(V v) const noexcept {
        assert(v < (V)size());
        return par_[v];
    }

    V index(V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    V operator[](V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    std::vector<std::pair<V, V>> decomp(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        std::vector<std::pair<V, V>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<V, V>> operator()(V s, V t) const {
        return decomp(s, t);
    }

    V lca(V u, V v) const {
        assert(u < (V)size());
        assert(v < (V)size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(V v, V p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    V levelAncestor(V v, usize step) const {
        assert(v < (V)size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    V jump(V s, V t, usize step) const {
        assert(s < (V)size());
        assert(t < (V)size());
        V uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    usize n_{};
    std::vector<V> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa

#include <unordered_set>
using namespace zawa;

RollingHashMonoidData::Value RollingHashMonoidData::base{
    RollingHashMonoidData::randomValue(26)
};

int main() {
    SetFastIO();
    int N;
    std::cin >> N;
    std::vector<char> K(N);
    for (auto& k : K) std::cin >> k;
    std::vector<std::vector<int>> T(N);
    for (int i{} ; i < N - 1 ; i++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        T[u].push_back(v);
        T[v].push_back(u);
    }
    HeavyLightDecomposition hld{T};
    std::vector<RollingHashMonoidData> init(N);
    for (int i{} ; i < N ; i++) {
        init[hld[i]] = RollingHashMonoidData{K[i]};
    }
    SegmentTree<RollingHashMonoid> seg{init};
    SegmentTree<ReverseOrder<RollingHashMonoid>> ges{init};
    int Q;
    std::cin >> Q;
    std::unordered_set<RollingHashMonoidData::Value> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int s, t;
            std::cin >> s >> t;
            s--; t--;
            RollingHashMonoidData res{};
            for (auto [u, v] : hld(s, t)) {
                u = hld[u];
                v = hld[v];
                if (u <= v) res = RollingHashMonoid::operation(res, seg.product(u, v + 1));
                else res = RollingHashMonoid::operation(res, ges.product(v, u + 1));
            }
            set.insert(res.hash);
            std::cout << set.size() << '\n';
        }
        else if (t == 2) {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            seg.assign(hld[k], RollingHashMonoidData{c});
            ges.assign(hld[k], RollingHashMonoidData{c});
        }
        else {
            assert(false);
        }
    }
}
accuracy: 1
time_ms: 90
memory_kb: 40248
final_rank: 6

code_snippet: #define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/0478"



#include <cstdint>
#include <cstddef>

namespace zawa {

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

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

using usize = std::size_t;

} // namespace zawa

#include <iostream>
#include <iomanip>

namespace zawa {

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

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

} // namespace zawa



namespace zawa {

// @reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
class Mersenne61ModInt {
public:
    using Value = u64;
private:
    static constexpr Value MOD{(1ull << 61) - 1}; // == MASK61
    static constexpr Value MASK30{(1ull << 30) - 1};
    static constexpr Value MASK31{(1ull << 31) - 1};
    Value v_{};
public:
    constexpr Mersenne61ModInt() {}

    static constexpr Value Mod() noexcept {
        return MOD;
    }
    static constexpr Value Modulo(const Value& v) noexcept {
        Value res{(v >> 61) + (v & MOD)};
        res = (res >= MOD ? res - MOD : res);
        return res;
    }
    static constexpr Value UnsafeMul(const Value& a, const Value& b) noexcept {
        Value fa{a >> 31}, fb{b >> 31};
        Value ba{a & MASK31}, bb{b & MASK31};
        Value mid{fa * bb + fb * ba};
        return Value{2}*fa*fb + (mid >> 30) + ((mid & MASK30) << 31) + ba*bb;
    }
    static constexpr Value Mul(const Value& a, const Value& b) noexcept {
        return Modulo(UnsafeMul(a, b));
    }
};

};

#include <random>
#include <type_traits>

namespace zawa {

struct RollingHashMonoidData {
    using Value = Mersenne61ModInt::Value;
    using Size = usize;
    static Value base;
    Value hash{}, pow{1};
    usize len{};

    constexpr RollingHashMonoidData() = default;
    constexpr RollingHashMonoidData(Value h, Value p, usize l) : hash{h}, pow{p}, len{l} {}
    template <class T>
    constexpr RollingHashMonoidData(const T& v) 
        : hash{static_cast<Value>(v)}, pow{base}, len{1} {}
    // RollingHashMonoidData(const RollingHashMonoidData& data)
    //     : hash{data.hash}, pow{data.pow}, len{data.len} {}
    
    static Value randomValue(const Value& sigma) {
        return std::mt19937{std::random_device{}()}() % (Mersenne61ModInt::Mod() - sigma) + sigma + 1;
    }

    friend constexpr bool operator==(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash == rhs.hash and lhs.len == rhs.len;
    }
    friend constexpr bool operator!=(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash != rhs.hash or lhs.len != rhs.len;
    }
};

struct RollingHashMonoid {
    using Modulo = Mersenne61ModInt;
    using Element = RollingHashMonoidData;
    static constexpr Element identity() noexcept {
        return Element{};
    }
    static constexpr Element operation(const Element& lhs, const Element& rhs) noexcept {
        return Element{
            Modulo::Modulo(Modulo::UnsafeMul(lhs.hash, rhs.pow) + rhs.hash),
            Modulo::Mul(lhs.pow, rhs.pow),
            lhs.len + rhs.len
        };
    }
};

} // namespace zawa



#include <concepts>

namespace zawa {

namespace concepts {

template <class T>
concept Semigroup = requires {
    typename T::Element;
    { T::operation(std::declval<typename T::Element>(), std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

} // namespace concepts

} // namespace zawa


namespace zawa {

namespace concepts {

template <class T>
concept Identitiable = requires {
    typename T::Element;
    { T::identity() } -> std::same_as<typename T::Element>;
};

template <class T>
concept Monoid = Semigroup<T> and Identitiable<T>;

} // namespace

} // namespace zawa

#include <vector>
#include <cassert>
#include <functional>
#include <ostream>

namespace zawa {

template <concepts::Monoid Monoid>
class SegmentTree {
public:

    using VM = Monoid;

    using V = typename VM::Element;

    using OM = Monoid;

    using O = typename OM::Element;

    SegmentTree() = default;

    explicit SegmentTree(usize n) : m_n{ n }, m_dat(n << 1, VM::identity()) {}

    explicit SegmentTree(const std::vector<V>& dat) : m_n{ dat.size() }, m_dat(dat.size() << 1, VM::identity()) {
        for (usize i{} ; i < m_n ; i++) {
            m_dat[i + m_n] = dat[i];
        }
        for (usize i{m_n} ; i-- ; i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    [[nodiscard]] inline usize size() const noexcept {
        return m_n;
    }

    [[nodiscard]] V get(usize i) const {
        assert(i < size());
        return m_dat[i + m_n];
    }

    [[nodiscard]] V operator[](usize i) const {
        assert(i < size());
        return m_dat[i + m_n];
    }

    void operation(usize i, const O& value) {
        assert(i < size());
        i += size();
        m_dat[i] = OM::operation(m_dat[i], value);
        while (i = parent(i), i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    void assign(usize i, const V& value) {
        assert(i < size());
        i += size();
        m_dat[i] = value;
        while (i = parent(i), i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    [[nodiscard]] V product(u32 l, u32 r) const {
        assert(l <= r and r <= size());
        V L{ VM::identity() }, R{ VM::identity() };
        for (l += size(), r += size() ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                L = VM::operation(L, m_dat[l++]);
            }
            if (r & 1) {
                R = VM::operation(m_dat[--r], R);
            }
        }
        return VM::operation(L, R);
    }

    template <class Function>
    [[nodiscard]] usize maxRight(usize l, const Function& f) {
        assert(l < size());
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(V)>>, "maxRight's argument f must be function bool(T)");
        assert(f(VM::identity()));
        usize res{l}, width{1};
        V prod{ VM::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += size() ; res + width <= size() ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(VM::operation(prod, m_dat[l]))) break; 
            res += width;
            prod = VM::operation(prod, m_dat[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= size() and f(VM::operation(prod, m_dat[l]))) {
                res += width;
                prod = VM::operation(prod, m_dat[l++]);
            } 
        }
        return res;
    }

    template <class Function>
    [[nodiscard]] usize minLeft(usize r, const Function& f) const {
        assert(r <= size());
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(V)>>, "minLeft's argument f must be function bool(T)");
        assert(f(VM::identity()));
        usize res{r}, width{1};
        V prod{ VM::identity() };
        for (r += size() ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(VM::operation(m_dat[r - 1], prod))) break;
            res -= width;
            prod = VM::operation(prod, m_dat[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(VM::operation(m_dat[r - 1], prod))) {
                res -= width;
                prod = VM::operation(m_dat[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (usize i{1} ; i < 2 * st.size() ; i++) {
            os << st.m_dat[i] << (i + 1 == 2 * st.size() ? "" : " ");
        }
        return os;
    }

private:

    constexpr u32 left(u32 v) const {
        return v << 1;
    }

    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }

    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize m_n;

    std::vector<V> m_dat;
};

} // namespace zawa


#include <algorithm>
#include <cmath>
#include <limits>
#include <utility>

namespace zawa {

template <class V>
class HeavyLightDecomposition {
public:

    static constexpr V Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(std::vector<std::vector<V>> T, V root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, V v, V p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (V x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, V v, V idx, V top) -> V {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(V v) const noexcept {
        assert(v < (V)size());
        return size_[v];
    }

    usize depth(V v) const noexcept {
        assert(v < (V)size());
        return dep_[v];
    }

    V parent(V v) const noexcept {
        assert(v < (V)size());
        return par_[v];
    }

    V index(V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    V operator[](V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    std::vector<std::pair<V, V>> decomp(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        std::vector<std::pair<V, V>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<V, V>> operator()(V s, V t) const {
        return decomp(s, t);
    }

    V lca(V u, V v) const {
        assert(u < (V)size());
        assert(v < (V)size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(V v, V p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    V levelAncestor(V v, usize step) const {
        assert(v < (V)size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    V jump(V s, V t, usize step) const {
        assert(s < (V)size());
        assert(t < (V)size());
        V uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    usize n_{};
    std::vector<V> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa

#include <unordered_set>
using namespace zawa;

RollingHashMonoidData::Value RollingHashMonoidData::base{
    RollingHashMonoidData::randomValue(26)
};

int main() {
    SetFastIO();
    int N;
    std::cin >> N;
    std::vector<char> K(N);
    for (auto& k : K) std::cin >> k;
    std::vector<std::vector<int>> T(N);
    for (int i{} ; i < N - 1 ; i++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        T[u].push_back(v);
        T[v].push_back(u);
    }
    HeavyLightDecomposition hld{T};
    std::vector<RollingHashMonoidData> init(N), tini(N);
    for (int i{} ; i < N ; i++) {
        init[hld[i]] = tini[N - hld[i] - 1] = RollingHashMonoidData{K[i]};
    }
    SegmentTree<RollingHashMonoid> seg{init}, ges{tini};
    int Q;
    std::cin >> Q;
    std::unordered_set<RollingHashMonoidData::Value> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int s, t;
            std::cin >> s >> t;
            s--; t--;
            RollingHashMonoidData res{};
            for (auto [u, v] : hld(s, t)) {
                u = hld[u];
                v = hld[v];
                if (u <= v) res = RollingHashMonoid::operation(res, seg.product(u, v + 1));
                else res = RollingHashMonoid::operation(res, ges.product(N - u - 1, N - v));
            }
            set.insert(res.hash);
            std::cout << set.size() << '\n';
        }
        else if (t == 2) {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            seg.assign(hld[k], RollingHashMonoidData{c});
            ges.assign(N - hld[k] - 1, RollingHashMonoidData{c});
        }
        else {
            assert(false);
        }
    }
}
accuracy: 1
time_ms: 90
memory_kb: 42580
final_rank: 9

code_snippet: #define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/0478"



#include <cstdint>
#include <cstddef>

namespace zawa {

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

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

using usize = std::size_t;

} // namespace zawa

#include <iostream>
#include <iomanip>

namespace zawa {

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

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

} // namespace zawa



namespace zawa {

// @reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
class Mersenne61ModInt {
public:
    using Value = u64;
private:
    static constexpr Value MOD{(1ull << 61) - 1}; // == MASK61
    static constexpr Value MASK30{(1ull << 30) - 1};
    static constexpr Value MASK31{(1ull << 31) - 1};
    Value v_{};
public:
    constexpr Mersenne61ModInt() {}

    static constexpr Value Mod() noexcept {
        return MOD;
    }
    static constexpr Value Modulo(const Value& v) noexcept {
        Value res{(v >> 61) + (v & MOD)};
        res = (res >= MOD ? res - MOD : res);
        return res;
    }
    static constexpr Value UnsafeMul(const Value& a, const Value& b) noexcept {
        Value fa{a >> 31}, fb{b >> 31};
        Value ba{a & MASK31}, bb{b & MASK31};
        Value mid{fa * bb + fb * ba};
        return Value{2}*fa*fb + (mid >> 30) + ((mid & MASK30) << 31) + ba*bb;
    }
    static constexpr Value Mul(const Value& a, const Value& b) noexcept {
        return Modulo(UnsafeMul(a, b));
    }
};

};

#include <random>
#include <type_traits>

namespace zawa {

struct RollingHashMonoidData {
    using Value = Mersenne61ModInt::Value;
    using Size = usize;
    static Value base;
    Value hash{}, pow{1};
    usize len{};

    constexpr RollingHashMonoidData() = default;
    constexpr RollingHashMonoidData(Value h, Value p, usize l) : hash{h}, pow{p}, len{l} {}
    template <class T>
    constexpr RollingHashMonoidData(const T& v) 
        : hash{static_cast<Value>(v)}, pow{base}, len{1} {}
    // RollingHashMonoidData(const RollingHashMonoidData& data)
    //     : hash{data.hash}, pow{data.pow}, len{data.len} {}
    
    static Value randomValue(const Value& sigma) {
        return std::mt19937{std::random_device{}()}() % (Mersenne61ModInt::Mod() - sigma) + sigma + 1;
    }

    friend constexpr bool operator==(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash == rhs.hash and lhs.len == rhs.len;
    }
    friend constexpr bool operator!=(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash != rhs.hash or lhs.len != rhs.len;
    }
};

struct RollingHashMonoid {
    using Modulo = Mersenne61ModInt;
    using Element = RollingHashMonoidData;
    static constexpr Element identity() noexcept {
        return Element{};
    }
    static constexpr Element operation(const Element& lhs, const Element& rhs) noexcept {
        return Element{
            Modulo::Modulo(Modulo::UnsafeMul(lhs.hash, rhs.pow) + rhs.hash),
            Modulo::Mul(lhs.pow, rhs.pow),
            lhs.len + rhs.len
        };
    }
};

} // namespace zawa



#include <concepts>

namespace zawa {

namespace concepts {

template <class T>
concept Semigroup = requires {
    typename T::Element;
    { T::operation(std::declval<typename T::Element>(), std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

} // namespace concepts

} // namespace zawa


namespace zawa {

namespace concepts {

template <class T>
concept Identitiable = requires {
    typename T::Element;
    { T::identity() } -> std::same_as<typename T::Element>;
};

template <class T>
concept Monoid = Semigroup<T> and Identitiable<T>;

} // namespace

} // namespace zawa

#include <vector>
#include <cassert>
#include <functional>
#include <ostream>

namespace zawa {

template <concepts::Monoid Monoid>
class SegmentTree {
public:
    using Value = typename Monoid::Element;
private:
    constexpr u32 left(u32 v) const {
        return v << 1;
    }
    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }
    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize n_;
    std::vector<Value> dat_;

public:
    SegmentTree() = default;
    SegmentTree(u32 n) : n_{ n }, dat_(n << 1, Monoid::identity()) {
        assert(n_);
    }
    SegmentTree(const std::vector<Value>& dat) : n_{ dat.size() }, dat_(dat.size() << 1, Monoid::identity()) {
        assert(n_);
        for (u32 i{} ; i < n_ ; i++) {
            dat_[i + n_] = dat[i];
        }
        for (u32 i{static_cast<u32>(n_) - 1} ; i ; i--) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value get(u32 i) const {
        assert(i < n_);
        return dat_[i + n_];
    }

    void set(u32 i, const Value& value) {
        assert(i < n_);
        i += n_;
        dat_[i] = value;
        while (i = parent(i), i) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value product(u32 l, u32 r) const {
        assert(l <= r and r <= n_);
        Value leftValue{ Monoid::identity() }, rightValue{ Monoid::identity() };
        for (l += n_, r += n_ ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                leftValue = Monoid::operation(leftValue, dat_[l++]);
            }
            if (r & 1) {
                rightValue = Monoid::operation(dat_[--r], rightValue);
            }
        }
        return Monoid::operation(leftValue, rightValue);
    }

    template <class Function>
    u32 maxRight(u32 l, const Function& f) {
        assert(l < n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "maxRight's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{l}, width{1};
        Value prod{ Monoid::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += n_ ; res + width <= n_ ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(Monoid::operation(prod, dat_[l]))) break; 
            res += width;
            prod = Monoid::operation(prod, dat_[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= n_ and f(Monoid::operation(prod, dat_[l]))) {
                res += width;
                prod = Monoid::operation(prod, dat_[l++]);
            } 
        }
        return res;
    }

    template <class Function>
    u32 minLeft(u32 r, const Function& f) const {
        assert(r <= n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "minLeft's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{r}, width{1};
        Value prod{ Monoid::identity() };
        for (r += n_ ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(Monoid::operation(dat_[r - 1], prod))) break;
            res -= width;
            prod = Monoid::operation(prod, dat_[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(Monoid::operation(dat_[r - 1], prod))) {
                res -= width;
                prod = Monoid::operation(dat_[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (u32 i{1} ; i < 2 * st.n_ ; i++) {
            os << st.dat_[i] << (i + 1 == 2 * st.n_ ? "" : " ");
        }
        return os;
    }
};

} // namespace zawa


#include <algorithm>
#include <cmath>
#include <limits>
#include <utility>

namespace zawa {

template <class V>
class HeavyLightDecomposition {
public:

    static constexpr V Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(std::vector<std::vector<V>> T, V root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, V v, V p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (V x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, V v, V idx, V top) -> V {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(V v) const noexcept {
        assert(v < (V)size());
        return size_[v];
    }

    usize depth(V v) const noexcept {
        assert(v < (V)size());
        return dep_[v];
    }

    V parent(V v) const noexcept {
        assert(v < (V)size());
        return par_[v];
    }

    V index(V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    V operator[](V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    std::vector<std::pair<V, V>> decomp(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        std::vector<std::pair<V, V>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<V, V>> operator()(V s, V t) const {
        return decomp(s, t);
    }

    V lca(V u, V v) const {
        assert(u < (V)size());
        assert(v < (V)size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(V v, V p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    V levelAncestor(V v, usize step) const {
        assert(v < (V)size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    V jump(V s, V t, usize step) const {
        assert(s < (V)size());
        assert(t < (V)size());
        V uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    usize n_{};
    std::vector<V> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa

#include <unordered_set>
using namespace zawa;

RollingHashMonoidData::Value RollingHashMonoidData::base{
    RollingHashMonoidData::randomValue(26)
};

int main() {
    SetFastIO();
    int N;
    std::cin >> N;
    std::vector<char> K(N);
    for (auto& k : K) std::cin >> k;
    std::vector<std::vector<int>> T(N);
    for (int i{} ; i < N - 1 ; i++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        T[u].push_back(v);
        T[v].push_back(u);
    }
    HeavyLightDecomposition hld{T};
    std::vector<RollingHashMonoidData> init(N), tini(N);
    for (int i{} ; i < N ; i++) {
        init[hld[i]] = tini[N - hld[i] - 1] = RollingHashMonoidData{K[i]};
    }
    SegmentTree<RollingHashMonoid> seg{init}, ges{tini};
    int Q;
    std::cin >> Q;
    std::unordered_set<RollingHashMonoidData::Value> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int s, t;
            std::cin >> s >> t;
            s--; t--;
            RollingHashMonoidData res{};
            for (auto [u, v] : hld(s, t)) {
                u = hld[u];
                v = hld[v];
                if (u <= v) res = RollingHashMonoid::operation(res, seg.product(u, v + 1));
                else res = RollingHashMonoid::operation(res, ges.product(N - u - 1, N - v));
            }
            set.insert(res.hash);
            std::cout << set.size() << '\n';
        }
        else if (t == 2) {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            seg.set(hld[k], RollingHashMonoidData{c});
            ges.set(N - hld[k] - 1, RollingHashMonoidData{c});
        }
        else {
            assert(false);
        }
    }
}
accuracy: 1
time_ms: 80
memory_kb: 42588
final_rank: 8

code_snippet: // AOJ #478 Tree Keyboard
// 2025.2.19

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
 
const ll MOD1 = 1000000007LL;
const ll MOD2 = 1000000009LL;
const ll BASE = 137;
 
const int MAXN = 100000;
 
vector<ll> pow1, pow2;
 
struct SegNode {
    int len;
    pair<ll,ll> hash;
    pair<ll,ll> revHash;
};
 
SegNode make_identity() {
    SegNode node;
    node.len = 0;
    node.hash = {0, 0};
    node.revHash = {0, 0};
    return node;
}
 
SegNode make_leaf(int val) {
    SegNode node;
    node.len = 1;
    node.hash = {val, val};
    node.revHash = {val, val};
    return node;
}
 
SegNode combineNode(const SegNode &L, const SegNode &R) {
    if(L.len == 0) return R;
    if(R.len == 0) return L;
    SegNode res;
    res.len = L.len + R.len;
    res.hash.first = (L.hash.first * pow1[R.len] % MOD1 + R.hash.first) % MOD1;
    res.hash.second = (L.hash.second * pow2[R.len] % MOD2 + R.hash.second) % MOD2;
    res.revHash.first = (R.revHash.first * pow1[L.len] % MOD1 + L.revHash.first) % MOD1;
    res.revHash.second = (R.revHash.second * pow2[L.len] % MOD2 + L.revHash.second) % MOD2;
    return res;
}
 
SegNode reverseNode(const SegNode &node) {
    SegNode res = node;
    swap(res.hash, res.revHash);
    return res;
}
 
struct SegTree {
    int n;
    vector<SegNode> tree;
    SegTree(int n) : n(n) {
        tree.resize(4*n);
    }
    void build(vector<int> &arr, int idx, int l, int r) {
        if(l == r) {
            tree[idx] = make_leaf(arr[l]);
            return;
        }
        int mid = (l + r) / 2;
        build(arr, idx*2, l, mid);
        build(arr, idx*2+1, mid+1, r);
        tree[idx] = combineNode(tree[idx*2], tree[idx*2+1]);
    }
    void update(int idx, int l, int r, int pos, int val) {
        if(l == r) {
            tree[idx] = make_leaf(val);
            return;
        }
        int mid = (l + r) / 2;
        if(pos <= mid)
            update(idx*2, l, mid, pos, val);
        else
            update(idx*2+1, mid+1, r, pos, val);
        tree[idx] = combineNode(tree[idx*2], tree[idx*2+1]);
    }
    SegNode query(int idx, int l, int r, int ql, int qr) {
        if(ql > r || qr < l) return make_identity();
        if(ql <= l && r <= qr) return tree[idx];
        int mid = (l + r) / 2;
        SegNode leftNode = query(idx*2, l, mid, ql, qr);
        SegNode rightNode = query(idx*2+1, mid+1, r, ql, qr);
        return combineNode(leftNode, rightNode);
    }
};
 
int N;
vector<vector<int>> adj;
vector<char> letter;
vector<int> parent, depth, heavy, head, pos;
 
int dfs(int v, int p) {
    parent[v] = p;
    depth[v] = (p == -1 ? 0 : depth[p] + 1);
    int size = 1;
    int maxSize = 0;
    heavy[v] = -1;
    for (int u : adj[v]) {
        if(u == p) continue;
        int subSize = dfs(u, v);
        if(subSize > maxSize) {
            maxSize = subSize;
            heavy[v] = u;
        }
        size += subSize;
    }
    return size;
}
 
int curPos = 0;
void decompose(int v, int h) {
    head[v] = h;
    pos[v] = curPos++;
    if(heavy[v] != -1) decompose(heavy[v], h);
    for (int u : adj[v]) {
        if(u == parent[v] || u == heavy[v]) continue;
        decompose(u, u);
    }
}
 
int LOGN;
vector<vector<int>> up;
 
void buildLCA() {
    LOGN = 0;
    while((1 << LOGN) <= N) LOGN++;
    up.assign(LOGN, vector<int>(N+1, -1));
    for (int v = 1; v <= N; v++)
        up[0][v] = (parent[v] == -1 ? v : parent[v]);
    for (int k = 1; k < LOGN; k++) {
        for (int v = 1; v <= N; v++)
            up[k][v] = up[k-1][ up[k-1][v] ];
    }
}
 
int LCA(int a, int b) {
    if(depth[a] < depth[b])
        swap(a, b);
    int d = depth[a] - depth[b];
    for(int k = 0; k < LOGN; k++){
        if(d & (1 << k)) a = up[k][a];
    }
    if(a == b) return a;
    for(int k = LOGN-1; k >= 0; k--){
        if(up[k][a] != up[k][b]){
            a = up[k][a];
            b = up[k][b];
        }
    }
    return parent[a];
}
 
SegNode queryUp(int u, int v, SegTree &segTree) {
    SegNode res = make_identity();
    while(head[u] != head[v]) {
        SegNode cur = segTree.query(1, 0, N-1, pos[head[u]], pos[u]);
        cur = reverseNode(cur);
        res = combineNode(res, cur);
        u = parent[head[u]];
    }
    SegNode cur = segTree.query(1, 0, N-1, pos[v], pos[u]);
    cur = reverseNode(cur);
    res = combineNode(res, cur);
    return res;
}
 
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
 
    cin >> N;
    letter.resize(N+1);
    for (int i = 1; i <= N; i++) cin >> letter[i];
    adj.assign(N+1, vector<int>());
    for (int i = 1; i < N; i++){
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
 
    parent.resize(N+1);
    depth.resize(N+1);
    heavy.resize(N+1);
    head.resize(N+1);
    pos.resize(N+1);
 
    dfs(1, -1);
    curPos = 0;
    decompose(1, 1);
    buildLCA();
 
    int maxPow = N + 5;
    pow1.resize(maxPow); pow2.resize(maxPow);
    pow1[0] = 1; pow2[0] = 1;
    for (int i = 1; i < maxPow; i++){
        pow1[i] = (pow1[i-1] * BASE) % MOD1;
        pow2[i] = (pow2[i-1] * BASE) % MOD2;
    }
 
    vector<int> baseArr(N);
    for (int v = 1; v <= N; v++) baseArr[pos[v]] = letter[v] - 'a' + 1;
 
    SegTree segTree(N);
    segTree.build(baseArr, 1, 0, N-1);
 
    unordered_set<ll> seen;
    seen.reserve(2e5);
 
    int Q;
    cin >> Q;
    for (int qi = 0; qi < Q; qi++){
        int type;
        cin >> type;
        if(type == 1) {
            int s, tnode;
            cin >> s >> tnode;
            int l = LCA(s, tnode);
            SegNode part1 = queryUp(s, l, segTree);
            SegNode part2 = queryUp(tnode, l, segTree);
            SegNode part2Forward = reverseNode(part2);
            if(part2Forward.len > 0) {
                int ltrVal = letter[l] - 'a' + 1;
                ll newHash1 = (part2Forward.hash.first - ltrVal * pow1[part2Forward.len - 1]) % MOD1;
                if(newHash1 < 0) newHash1 += MOD1;
                ll newHash2 = (part2Forward.hash.second - ltrVal * pow2[part2Forward.len - 1]) % MOD2;
                if(newHash2 < 0) newHash2 += MOD2;
                SegNode part2Trim;
                part2Trim.len = part2Forward.len - 1;
                part2Trim.hash = {newHash1, newHash2};
                static ll invBase1 = 0, invBase2 = 0;
                if(invBase1 == 0){
                    auto modExp = [&](ll base, ll exp, ll mod) -> ll {
                        ll result = 1;
                        base %= mod;
                        while(exp > 0){
                            if(exp & 1) result = (result * base) % mod;
                            base = (base * base) % mod;
                            exp >>= 1;
                        }
                        return result;
                    };
                    invBase1 = modExp(BASE, MOD1-2, MOD1);
                    invBase2 = modExp(BASE, MOD2-2, MOD2);
                }
                ll rtemp1 = (part2Forward.revHash.first - ltrVal) % MOD1;
                if(rtemp1 < 0) rtemp1 += MOD1;
                ll rtemp2 = (part2Forward.revHash.second - ltrVal) % MOD2;
                if(rtemp2 < 0) rtemp2 += MOD2;
                ll newRev1 = (rtemp1 * invBase1) % MOD1;
                ll newRev2 = (rtemp2 * invBase2) % MOD2;
                part2Trim.revHash = {newRev1, newRev2};
 
                SegNode finalNode = combineNode(part1, part2Trim);
                ll key = ((finalNode.hash.second & 0xffffffffLL) << 32) | (finalNode.hash.first & 0xffffffffLL);
                seen.insert(key);
            } else {
                SegNode finalNode = part1;
                ll key = ((finalNode.hash.second & 0xffffffffLL) << 32) | (finalNode.hash.first & 0xffffffffLL);
                seen.insert(key);
            }
            cout << seen.size() << endl;
        } else if(type == 2) {
            int k; char c;
            cin >> k >> c;
            letter[k] = c;
            int val = c - 'a' + 1;
            segTree.update(1, 0, N-1, pos[k], val);
        }
    }
    return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 47444
final_rank: 14

code_snippet: #line 1 "0478.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/0478"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/TypeAlias.hpp"

#include <cstdint>
#include <cstddef>

namespace zawa {

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

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

using usize = std::size_t;

} // namespace zawa
#line 4 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#include <iostream>
#include <iomanip>

namespace zawa {

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

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

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/Algebra/Monoid/RollingHashMonoid.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Number/Mersenne61ModInt.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/Number/Mersenne61ModInt.hpp"

namespace zawa {

// @reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
class Mersenne61ModInt {
public:
    using Value = u64;
private:
    static constexpr Value MOD{(1ull << 61) - 1}; // == MASK61
    static constexpr Value MASK30{(1ull << 30) - 1};
    static constexpr Value MASK31{(1ull << 31) - 1};
    Value v_{};
public:
    constexpr Mersenne61ModInt() {}

    static constexpr Value Mod() noexcept {
        return MOD;
    }
    static constexpr Value Modulo(const Value& v) noexcept {
        Value res{(v >> 61) + (v & MOD)};
        res = (res >= MOD ? res - MOD : res);
        return res;
    }
    static constexpr Value UnsafeMul(const Value& a, const Value& b) noexcept {
        Value fa{a >> 31}, fb{b >> 31};
        Value ba{a & MASK31}, bb{b & MASK31};
        Value mid{fa * bb + fb * ba};
        return Value{2}*fa*fb + (mid >> 30) + ((mid & MASK30) << 31) + ba*bb;
    }
    static constexpr Value Mul(const Value& a, const Value& b) noexcept {
        return Modulo(UnsafeMul(a, b));
    }
};

};
#line 5 "/home/zawatin/compro/cp-documentation/Src/Algebra/Monoid/RollingHashMonoid.hpp"

#include <random>
#include <type_traits>

namespace zawa {

struct RollingHashMonoidData {
    using Value = Mersenne61ModInt::Value;
    using Size = usize;
    static Value base;
    Value hash{}, pow{1};
    usize len{};

    constexpr RollingHashMonoidData() = default;
    constexpr RollingHashMonoidData(Value h, Value p, usize l) : hash{h}, pow{p}, len{l} {}
    template <class T>
    constexpr RollingHashMonoidData(const T& v) 
        : hash{static_cast<Value>(v)}, pow{base}, len{1} {}
    // RollingHashMonoidData(const RollingHashMonoidData& data)
    //     : hash{data.hash}, pow{data.pow}, len{data.len} {}
    
    static Value randomValue(const Value& sigma) {
        return std::mt19937{std::random_device{}()}() % (Mersenne61ModInt::Mod() - sigma) + sigma + 1;
    }

    friend constexpr bool operator==(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash == rhs.hash and lhs.len == rhs.len;
    }
    friend constexpr bool operator!=(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash != rhs.hash or lhs.len != rhs.len;
    }
};

struct RollingHashMonoid {
    using Modulo = Mersenne61ModInt;
    using Element = RollingHashMonoidData;
    static constexpr Element identity() noexcept {
        return Element{};
    }
    static constexpr Element operation(const Element& lhs, const Element& rhs) noexcept {
        return Element{
            Modulo::Modulo(Modulo::UnsafeMul(lhs.hash, rhs.pow) + rhs.hash),
            Modulo::Mul(lhs.pow, rhs.pow),
            lhs.len + rhs.len
        };
    }
};

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/DataStructure/SegmentTree/SegmentTree.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Algebra/Monoid/MonoidConcept.hpp"

#include <concepts>

namespace zawa {

namespace Concept {

template <class T>
concept Monoid = requires {
    typename T::Element;
    { T::identity() } -> std::same_as<typename T::Element>;
    { T::operation(std::declval<typename T::Element>(), std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

} // namespace

} // namespace zawa
#line 5 "/home/zawatin/compro/cp-documentation/Src/DataStructure/SegmentTree/SegmentTree.hpp"

#include <vector>
#include <cassert>
#include <functional>
#line 10 "/home/zawatin/compro/cp-documentation/Src/DataStructure/SegmentTree/SegmentTree.hpp"
#include <ostream>

namespace zawa {

template <Concept::Monoid Monoid>
class SegmentTree {
public:
    using Value = typename Monoid::Element;
private:
    constexpr u32 left(u32 v) const {
        return v << 1;
    }
    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }
    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize n_;
    std::vector<Value> dat_;

public:
    SegmentTree() = default;
    SegmentTree(u32 n) : n_{ n }, dat_(n << 1, Monoid::identity()) {
        assert(n_);
    }
    SegmentTree(const std::vector<Value>& dat) : n_{ dat.size() }, dat_(dat.size() << 1, Monoid::identity()) {
        assert(n_);
        for (u32 i{} ; i < n_ ; i++) {
            dat_[i + n_] = dat[i];
        }
        for (u32 i{static_cast<u32>(n_) - 1} ; i ; i--) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value get(u32 i) const {
        assert(i < n_);
        return dat_[i + n_];
    }

    void set(u32 i, const Value& value) {
        assert(i < n_);
        i += n_;
        dat_[i] = value;
        while (i = parent(i), i) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value product(u32 l, u32 r) const {
        assert(l <= r and r <= n_);
        Value leftValue{ Monoid::identity() }, rightValue{ Monoid::identity() };
        for (l += n_, r += n_ ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                leftValue = Monoid::operation(leftValue, dat_[l++]);
            }
            if (r & 1) {
                rightValue = Monoid::operation(dat_[--r], rightValue);
            }
        }
        return Monoid::operation(leftValue, rightValue);
    }

    template <class Function>
    u32 maxRight(u32 l, const Function& f) {
        assert(l < n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "maxRight's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{l}, width{1};
        Value prod{ Monoid::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += n_ ; res + width <= n_ ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(Monoid::operation(prod, dat_[l]))) break; 
            res += width;
            prod = Monoid::operation(prod, dat_[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= n_ and f(Monoid::operation(prod, dat_[l]))) {
                res += width;
                prod = Monoid::operation(prod, dat_[l++]);
            } 
        }
        return res;
    }

    template <class Function>
    u32 minLeft(u32 r, const Function& f) const {
        assert(r <= n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "minLeft's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{r}, width{1};
        Value prod{ Monoid::identity() };
        for (r += n_ ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(Monoid::operation(dat_[r - 1], prod))) break;
            res -= width;
            prod = Monoid::operation(prod, dat_[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(Monoid::operation(dat_[r - 1], prod))) {
                res -= width;
                prod = Monoid::operation(dat_[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (u32 i{1} ; i < 2 * st.n_ ; i++) {
            os << st.dat_[i] << (i + 1 == 2 * st.n_ ? "" : " ");
        }
        return os;
    }
};

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"

#include <algorithm>
#line 7 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"
#include <cmath>
#include <limits>
#include <utility>
#line 11 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"

namespace zawa {

template <class V>
class HeavyLightDecomposition {
public:

    static constexpr V Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(std::vector<std::vector<V>> T, V root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, V v, V p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (V x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, V v, V idx, V top) -> V {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(V v) const noexcept {
        assert(v < (V)size());
        return size_[v];
    }

    usize depth(V v) const noexcept {
        assert(v < (V)size());
        return dep_[v];
    }

    V parent(V v) const noexcept {
        assert(v < (V)size());
        return par_[v];
    }

    V index(V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    V operator[](V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    std::vector<std::pair<V, V>> decomp(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        std::vector<std::pair<V, V>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<V, V>> operator()(V s, V t) const {
        return decomp(s, t);
    }

    V lca(V u, V v) const {
        assert(u < (V)size());
        assert(v < (V)size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(V v, V p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    V levelAncestor(V v, usize step) const {
        assert(v < (V)size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    V jump(V s, V t, usize step) const {
        assert(s < (V)size());
        assert(t < (V)size());
        V uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    usize n_{};
    std::vector<V> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa
#line 7 "0478.test.cpp"

#line 11 "0478.test.cpp"
#include <unordered_set>
using namespace zawa;

RollingHashMonoidData::Value RollingHashMonoidData::base{
    RollingHashMonoidData::randomValue(26)
};

int main() {
    SetFastIO();
    int N;
    std::cin >> N;
    std::vector<char> K(N);
    for (auto& k : K) std::cin >> k;
    std::vector<std::vector<int>> T(N);
    for (int i{} ; i < N - 1 ; i++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        T[u].push_back(v);
        T[v].push_back(u);
    }
    HeavyLightDecomposition hld{T};
    std::vector<RollingHashMonoidData> init(N), tini(N);
    for (int i{} ; i < N ; i++) {
        init[hld[i]] = tini[N - hld[i] - 1] = RollingHashMonoidData{K[i]};
    }
    SegmentTree<RollingHashMonoid> seg{init}, ges{tini};
    int Q;
    std::cin >> Q;
    std::unordered_set<RollingHashMonoidData::Value> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int s, t;
            std::cin >> s >> t;
            s--; t--;
            RollingHashMonoidData res{};
            for (auto [u, v] : hld(s, t)) {
                u = hld[u];
                v = hld[v];
                if (u <= v) res = RollingHashMonoid::operation(res, seg.product(u, v + 1));
                else res = RollingHashMonoid::operation(res, ges.product(N - u - 1, N - v));
            }
            set.insert(res.hash);
            std::cout << set.size() << '\n';
        }
        else if (t == 2) {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            seg.set(hld[k], RollingHashMonoidData{c});
            ges.set(N - hld[k] - 1, RollingHashMonoidData{c});
        }
        else {
            assert(false);
        }
    }
}
accuracy: 1
time_ms: 90
memory_kb: 42592
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
vector<ll> mod = {(ll)998244353,(ll)1000000007,(ll)1000000009,(ll)1000000021,(ll)1000000033};
ll power(ll a,ll b,ll m){
  ll res = 1;
  while(b>0){
    if(b%2==0){
      b/=2;
      a*=a;
      a%=m;
    }else{
      b--;
      res*=a;
      res%=m;
    }
  }
  return res;
}
struct segtree{
    int n;
    vector<ll> node;
    segtree(int N){
        n = 1;
        while(n<N) n*=2;
        node.resize(2*n-1);
    }
    void update(int a,ll x,ll m){
        int i = a+n-1;
        node[i] = x;
        while(i>0){
            i = (i-1)/2;
            node[i] = (node[i*2+1]+node[i*2+2])%m;
        }
    }
    ll find(int a,int b,ll m,int k=0,int l=0,int r=-1){
        if(r<0) r = n;
        if(b<=l||r<=a) return 0;
        if(a<=l&&r<=b) return node[k];
        ll vl = find(a,b,m,k*2+1,l,(l+r)/2);
        ll vr = find(a,b,m,k*2+2,(l+r)/2,r);
        return (vl+vr)%m;
    }
};
int main(){
    int N;
    cin >> N;
    mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<ll> rand(1, mod[0] - 1);
    uniform_int_distribution<ll> rand1(1, mod[1] - 1);
    uniform_int_distribution<ll> rand2(1, mod[2] - 1);
    uniform_int_distribution<ll> rand3(1, mod[3] - 1);
    uniform_int_distribution<ll> rand4(1, mod[4] - 1);
    vector<ll> base = {rand(mt),rand1(mt),rand2(mt),rand3(mt),rand4(mt)};
    vector<vector<ll>> x(5,vector<ll>(N));
    for(int i=0;i<5;i++) x[i][0] = 1;
    for(int i=0;i<5;i++){
      for(int j=1;j<N;j++){
        x[i][j] = (x[i][j-1]*base[i])%mod[i];
      }
    }
    vector<vector<ll>> px(5,vector<ll>(N));
    for(int i=0;i<5;i++) px[i][0] = 1;
    for(int i=0;i<5;i++){
      for(int j=1;j<N;j++){
        px[i][j] = (px[i][j-1]*power(base[i],mod[i]-2,mod[i]))%mod[i];
      }
    }
    vector<vector<int>> G(N);
    vector<ll> cha(N);
    for(int i=0;i<N;i++){
      char k;
      cin >> k;
      cha[i] = k-'a'+1;
    }
    for(int i=0;i<N-1;i++){
        int u,v;
        cin >> u >> v;
        u--;
        v--;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vector<int> inv(N);
    vector<int> outv(N);
    vector<ll> depth(N);
    vector<ll> revdepth(N);
    vector<int> parent(N);
    depth[0] = 0;
    parent[0] = -1;
    int num = 0;
    auto dfs = [&](auto dfs,int u,int p)->void{
        inv[u] = num++;
        for(auto v:G[u]){
            if(v==p) continue;
            depth[v] = depth[u]+1;
            parent[v] = u;
            dfs(dfs,v,u);
        }
        outv[u] = num++;
    };
    dfs(dfs,0,-1);
    for(int i=0;i<N;i++){
      revdepth[i] = N-depth[i]-1;
    }
    vector<segtree> seg(5,2*N);
    vector<segtree> seg1(5,2*N);
    for(int i=0;i<5;i++){
      for(int j=0;j<N;j++){
        seg[i].update(inv[j],((cha[j])*x[i][depth[j]])%mod[i],mod[i]);
        seg[i].update(outv[j],mod[i]-((cha[j])*x[i][depth[j]])%mod[i],mod[i]);
        seg1[i].update(inv[j],((cha[j])*x[i][revdepth[j]])%mod[i],mod[i]);
        seg1[i].update(outv[j],mod[i]-((cha[j])*x[i][revdepth[j]])%mod[i],mod[i]);
      }
    }
    vector<vector<int>> table(20,vector<int>(N));
    table[0] = parent;
    for(int i=0;i<19;i++){
        for(int j=0;j<N;j++){
            if(table[i][j]==-1) table[i+1][j] = -1;
            else table[i+1][j] = table[i][table[i][j]];
        }
    }
    auto calcLCA = [&](int a,int b)->int{
        if(depth[a]>depth[b]) swap(a,b);
        for(int i=19;i>=0;i--){
            int o = table[i][b];
            if(o==-1) continue;
            if(depth[o]>=depth[a]){
                b = o;
            }
        }
        if(a==b) return a;
        for(int i=19;i>=0;i--){
            int na = table[i][a];
            int nb = table[i][b];
            if(na!=nb){
                a = na;
                b = nb;
            }
        }
        return parent[b];
    };
    int Q;
    cin >> Q;
    set<vector<ll>> st;
    for(int i=0;i<Q;i++){
        int n;
        cin >> n;
        if(n==1){
            int s,t;
            cin >> s >> t;
            s--;
            t--;
            vector<ll> v;
            for(int j=0;j<5;j++){
              int lca = calcLCA(s,t);
              ll sl = seg1[j].find(inv[lca],inv[s]+1,mod[j])*(px[j][revdepth[s]]);
              sl %= mod[j];
              ll lt = seg[j].find(inv[lca],inv[t]+1,mod[j]);
              lt -= seg[j].find(inv[lca],inv[lca]+1,mod[j]);
              lt+=mod[j];
              lt %= mod[j];
              lt *= px[j][depth[lca]];
              lt %= mod[j];
              lt *= x[j][depth[s]-depth[lca]];
              lt %= mod[j];
              v.push_back((sl+lt)%mod[j]);
            }
            st.insert(v);
            cout << st.size() << endl;
        }else{
            int k;
            char c;
            cin >> k >> c;
            k--;
            cha[k] = c-'a'+1;
            for(int j=0;j<5;j++){
              seg[j].update(inv[k],((cha[k])*x[j][depth[k]])%mod[j],mod[j]);
              seg[j].update(outv[k],mod[j]-((cha[k])*x[j][depth[k]])%mod[j],mod[j]);
              seg1[j].update(inv[k],((cha[k])*x[j][revdepth[k]])%mod[j],mod[j]);
              seg1[j].update(outv[k],mod[j]-((cha[k])*x[j][revdepth[k]])%mod[j],mod[j]);
            }
        }
    }
}
accuracy: 1
time_ms: 1430
memory_kb: 92188
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll mod = 1000000007;
ll power(ll a,ll b){
  ll res = 1;
  while(b>0){
    if(b%2==0){
      b/=2;
      a*=a;
      a%=mod;
    }else{
      b--;
      res*=a;
      res%=mod;
    }
  }
  return res;
}
struct segtree{
    int n;
    vector<ll> node;
    segtree(int N){
        n = 1;
        while(n<N) n*=2;
        node.resize(2*n-1);
    }
    void update(int a,int x){
        int i = a+n-1;
        node[i] = x;
        while(i>0){
            i = (i-1)/2;
            node[i] = (node[i*2+1]+node[i*2+2])%mod;
        }
    }
    ll find(int a,int b,int k=0,int l=0,int r=-1){
        if(r<0) r = n;
        if(b<=l||r<=a) return 0;
        if(a<=l&&r<=b) return node[k];
        ll vl = find(a,b,k*2+1,l,(l+r)/2);
        ll vr = find(a,b,k*2+2,(l+r)/2,r);
        return (vl+vr)%mod;
    }
};
int main(){
    int N;
    cin >> N;
    mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<ll> rand(1, mod - 1);
    ll base = rand(mt);
    vector<ll> x(N);
    x[0] = 1;
    for(int i=1;i<N;i++){
      x[i] = (x[i-1]*base)%mod;
    }
    vector<ll> px(N);
    px[0] = 1;
    for(int i=1;i<N;i++){
      px[i] = px[i-1]*power(base,mod-2);
      px[i] %= mod;
    }
    vector<vector<int>> G(N);
    vector<ll> cha(N);
    for(int i=0;i<N;i++){
      char k;
      cin >> k;
      cha[i] = k-'a'+1;
    }
    for(int i=0;i<N-1;i++){
        int u,v;
        cin >> u >> v;
        u--;
        v--;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vector<int> inv(N);
    vector<int> outv(N);
    vector<ll> depth(N);
    vector<ll> revdepth(N);
    vector<int> parent(N);
    depth[0] = 0;
    parent[0] = -1;
    int num = 0;
    auto dfs = [&](auto dfs,int u,int p)->void{
        inv[u] = num++;
        for(auto v:G[u]){
            if(v==p) continue;
            depth[v] = depth[u]+1;
            parent[v] = u;
            dfs(dfs,v,u);
        }
        outv[u] = num++;
    };
    dfs(dfs,0,-1);
    for(int i=0;i<N;i++){
      revdepth[i] = N-depth[i]-1;
    }
    segtree seg(2*N);
    segtree seg1(2*N);
    for(int i=0;i<N;i++){
        seg.update(inv[i],((cha[i])*x[depth[i]])%mod);
        seg.update(outv[i],mod-((cha[i])*x[depth[i]])%mod);
        seg1.update(inv[i],((cha[i])*x[revdepth[i]])%mod);
        seg1.update(outv[i],mod-((cha[i])*x[revdepth[i]])%mod);
    }
    vector<vector<int>> table(20,vector<int>(N));
    table[0] = parent;
    for(int i=0;i<19;i++){
        for(int j=0;j<N;j++){
            if(table[i][j]==-1) table[i+1][j] = -1;
            else table[i+1][j] = table[i][table[i][j]];
        }
    }
    auto calcLCA = [&](int a,int b)->int{
        if(depth[a]>depth[b]) swap(a,b);
        for(int i=19;i>=0;i--){
            int o = table[i][b];
            if(o==-1) continue;
            if(depth[o]>=depth[a]){
                b = o;
            }
        }
        if(a==b) return a;
        for(int i=19;i>=0;i--){
            int na = table[i][a];
            int nb = table[i][b];
            if(na!=nb){
                a = na;
                b = nb;
            }
        }
        return parent[b];
    };
    int Q;
    cin >> Q;
    int ans = 0;
    set<ll> st;
    for(int i=0;i<Q;i++){
        int n;
        cin >> n;
        if(n==1){
            int s,t;
            cin >> s >> t;
            s--;
            t--;
            int lca = calcLCA(s,t);
            ll sl = seg1.find(inv[lca],inv[s]+1)*(px[revdepth[s]]);
            sl %= mod;
            ll lt = seg.find(inv[lca],inv[t]+1);
            lt -= seg.find(inv[lca],inv[lca]+1);
            lt+=mod;
            lt %= mod;
            lt *= px[depth[lca]];
            lt %= mod;
            lt *= x[depth[s]-depth[lca]];
            lt %= mod;
            st.insert((sl+lt)%mod);
            cout << st.size() << endl;
        }else{
            int k;
            char c;
            cin >> k >> c;
            k--;
            cha[k] = c-'a'+1;
            seg.update(inv[k],((cha[k])*x[depth[k]])%mod);
            seg.update(outv[k],mod-((cha[k])*x[depth[k]])%mod);
            seg1.update(inv[k],((cha[k])*x[revdepth[k]])%mod);
            seg1.update(outv[k],mod-((cha[k])*x[revdepth[k]])%mod);
        }
    }
}
accuracy: 0.5882352941176471
time_ms: 380
memory_kb: 31992
final_rank: 20

code_snippet: #line 1 "0478.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/0478"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Template/TypeAlias.hpp"

#include <cstdint>
#include <cstddef>

namespace zawa {

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

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

using usize = std::size_t;

} // namespace zawa
#line 4 "/home/zawatin/compro/cp-documentation/Src/Template/IOSetting.hpp"

#include <iostream>
#include <iomanip>

namespace zawa {

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

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

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/Algebra/Monoid/RollingHashMonoid.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Number/Mersenne61ModInt.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/Number/Mersenne61ModInt.hpp"

namespace zawa {

// @reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
class Mersenne61ModInt {
public:
    using Value = u64;
private:
    static constexpr Value MOD{(1ull << 61) - 1}; // == MASK61
    static constexpr Value MASK30{(1ull << 30) - 1};
    static constexpr Value MASK31{(1ull << 31) - 1};
    Value v_{};
public:
    constexpr Mersenne61ModInt() {}

    static constexpr Value Mod() noexcept {
        return MOD;
    }
    static constexpr Value Modulo(const Value& v) noexcept {
        Value res{(v >> 61) + (v & MOD)};
        res = (res >= MOD ? res - MOD : res);
        return res;
    }
    static constexpr Value UnsafeMul(const Value& a, const Value& b) noexcept {
        Value fa{a >> 31}, fb{b >> 31};
        Value ba{a & MASK31}, bb{b & MASK31};
        Value mid{fa * bb + fb * ba};
        return Value{2}*fa*fb + (mid >> 30) + ((mid & MASK30) << 31) + ba*bb;
    }
    static constexpr Value Mul(const Value& a, const Value& b) noexcept {
        return Modulo(UnsafeMul(a, b));
    }
};

};
#line 5 "/home/zawatin/compro/cp-documentation/Src/Algebra/Monoid/RollingHashMonoid.hpp"

#include <random>
#include <type_traits>

namespace zawa {

struct RollingHashMonoidData {
    using Value = Mersenne61ModInt::Value;
    using Size = usize;
    static Value base;
    Value hash{}, pow{1};
    usize len{};

    constexpr RollingHashMonoidData() = default;
    constexpr RollingHashMonoidData(Value h, Value p, usize l) : hash{h}, pow{p}, len{l} {}
    template <class T>
    constexpr RollingHashMonoidData(const T& v) 
        : hash{static_cast<Value>(v)}, pow{base}, len{1} {}
    
    static Value randomValue(const Value& sigma) {
        return std::mt19937{std::random_device{}()}() % (Mersenne61ModInt::Mod() - sigma) + sigma + 1;
    }

    friend constexpr bool operator==(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash == rhs.hash and lhs.len == rhs.len;
    }
    friend constexpr bool operator!=(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash != rhs.hash or lhs.len != rhs.len;
    }
};

struct RollingHashMonoid {
    using Modulo = Mersenne61ModInt;
    using Element = RollingHashMonoidData;
    static constexpr Element identity() noexcept {
        return Element{};
    }
    static constexpr Element operation(const Element& lhs, const Element& rhs) noexcept {
        return Element{
            Modulo::Modulo(Modulo::UnsafeMul(lhs.hash, rhs.pow) + rhs.hash),
            Modulo::Mul(lhs.pow, rhs.pow),
            lhs.len + rhs.len
        };
    }
};

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/DataStructure/SegmentTree/SegmentTree.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/DataStructure/SegmentTree/SegmentTree.hpp"

#include <vector>
#include <cassert>
#include <functional>
#line 9 "/home/zawatin/compro/cp-documentation/Src/DataStructure/SegmentTree/SegmentTree.hpp"
#include <ostream>

namespace zawa {

template <class Monoid>
class SegmentTree {
public:
    using Value = typename Monoid::Element;
private:
    constexpr u32 left(u32 v) const {
        return v << 1;
    }
    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }
    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize n_;
    std::vector<Value> dat_;

public:
    SegmentTree() = default;
    SegmentTree(u32 n) : n_{ n }, dat_(n << 1, Monoid::identity()) {
        assert(n_);
    }
    SegmentTree(const std::vector<Value>& dat) : n_{ dat.size() }, dat_(dat.size() << 1, Monoid::identity()) {
        assert(n_);
        for (u32 i{} ; i < n_ ; i++) {
            dat_[i + n_] = dat[i];
        }
        for (u32 i{static_cast<u32>(n_) - 1} ; i ; i--) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value get(u32 i) const {
        assert(i < n_);
        return dat_[i + n_];
    }

    void set(u32 i, const Value& value) {
        assert(i < n_);
        i += n_;
        dat_[i] = value;
        while (i = parent(i), i) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value product(u32 l, u32 r) const {
        assert(l < n_);
        assert(l <= r and r <= n_);
        Value leftValue{ Monoid::identity() }, rightValue{ Monoid::identity() };
        for (l += n_, r += n_ ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                leftValue = Monoid::operation(leftValue, dat_[l++]);
            }
            if (r & 1) {
                rightValue = Monoid::operation(dat_[--r], rightValue);
            }
        }
        return Monoid::operation(leftValue, rightValue);
    }

    template <class Function>
    u32 maxRight(u32 l, const Function& f) {
        assert(l < n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "maxRight's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{l}, width{1};
        Value prod{ Monoid::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += n_ ; res + width <= n_ ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(Monoid::operation(prod, dat_[l]))) break; 
            res += width;
            prod = Monoid::operation(prod, dat_[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= n_ and f(Monoid::operation(prod, dat_[l]))) {
                res += width;
                prod = Monoid::operation(prod, dat_[l++]);
            } 
        }
        return res;
    }

    template <class Function>
    u32 minLeft(u32 r, const Function& f) const {
        assert(r <= n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "minLeft's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{r}, width{1};
        Value prod{ Monoid::identity() };
        for (r += n_ ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(Monoid::operation(dat_[r - 1], prod))) break;
            res -= width;
            prod = Monoid::operation(prod, dat_[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(Monoid::operation(dat_[r - 1], prod))) {
                res -= width;
                prod = Monoid::operation(dat_[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (u32 i{1} ; i < 2 * st.n_ ; i++) {
            os << st.dat_[i] << (i + 1 == 2 * st.n_ ? "" : " ");
        }
        return os;
    }
};

} // namespace zawa
#line 2 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"

#line 2 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/Tree.hpp"

#line 4 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/Tree.hpp"

#line 7 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/Tree.hpp"

namespace zawa {

using Tree = std::vector<std::vector<u32>>;

void AddEdge(Tree& T, u32 u, u32 v) {
    assert(u < T.size());
    assert(v < T.size());
    T[u].emplace_back(v);
    T[v].emplace_back(u);
}

void AddDirectedEdge(Tree& T, u32 from, u32 to) {
    assert(from < T.size());
    assert(to < T.size());
    T[from].emplace_back(to);
}

} // namespace zawa
#line 5 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"

#include <algorithm>
#line 8 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"
#include <cmath>
#include <limits>
#include <utility>
#line 12 "/home/zawatin/compro/cp-documentation/Src/Graph/Tree/HeavyLightDecomposition.hpp"

namespace zawa {

class HeavyLightDecomposition {
public:
    using Vertex = u32;

    static constexpr Vertex Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(Tree T, Vertex root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, Vertex v, Vertex p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (Vertex x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, Vertex v, Vertex idx, Vertex top) -> Vertex {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(Vertex v) const noexcept {
        assert(v < size());
        return size_[v];
    }

    usize depth(Vertex v) const noexcept {
        assert(v < size());
        return dep_[v];
    }

    Vertex parent(Vertex v) const noexcept {
        assert(v < size());
        return par_[v];
    }

    Vertex index(Vertex v) const noexcept {
        assert(v < size());
        return idx_[v];
    }

    Vertex operator[](Vertex v) const noexcept {
        assert(v < size());
        return idx_[v];
    }


    std::vector<std::pair<Vertex, Vertex>> decomp(Vertex s, Vertex t) const {
        assert(s < size());
        assert(t < size());
        std::vector<std::pair<Vertex, Vertex>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<Vertex, Vertex>> operator()(Vertex s, Vertex t) const {
        return decomp(s, t);
    }

    Vertex lca(u32 u, u32 v) const {
        assert(u < size());
        assert(v < size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(Vertex v, Vertex p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    Vertex levelAncestor(Vertex v, usize step) const {
        assert(v < size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    Vertex jump(Vertex s, Vertex t, usize step) const {
        assert(s < size());
        assert(t < size());
        Vertex uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(Vertex s, Vertex t) const {
        assert(s < size());
        assert(t < size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr Vertex INVALID{static_cast<Vertex>(-1)};
    usize n_{};
    std::vector<Vertex> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa
#line 7 "0478.test.cpp"

#line 11 "0478.test.cpp"
#include <unordered_set>
using namespace zawa;

RollingHashMonoidData::Value RollingHashMonoidData::base{
    RollingHashMonoidData::randomValue(26)
};

int main() {
    SetFastIO();
    int N;
    std::cin >> N;
    std::vector<char> K(N);
    for (auto& k : K) std::cin >> k;
    Tree T(N);
    for (int i{} ; i < N - 1 ; i++) {
        int u, v;
        std::cin >> u >> v;
        AddEdge(T, u - 1, v - 1);
    }
    HeavyLightDecomposition hld{T};
    std::vector<RollingHashMonoidData> init(N), tini(N);
    for (int i{} ; i < N ; i++) {
        init[hld[i]] = tini[N - hld[i] - 1] = RollingHashMonoidData{K[i]};
    }
    SegmentTree<RollingHashMonoid> seg{init}, ges{tini};
    int Q;
    std::cin >> Q;
    std::unordered_set<RollingHashMonoidData::Value> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int s, t;
            std::cin >> s >> t;
            s--; t--;
            RollingHashMonoidData res{};
            for (auto [u, v] : hld(s, t)) {
                u = hld[u];
                v = hld[v];
                if (u <= v) res = RollingHashMonoid::operation(res, seg.product(u, v + 1));
                else res = RollingHashMonoid::operation(res, ges.product(N - u - 1, N - v));
            }
            set.insert(res.hash);
            std::cout << set.size() << '\n';
        }
        else if (t == 2) {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            seg.set(hld[k], RollingHashMonoidData{c});
            ges.set(N - hld[k] - 1, RollingHashMonoidData{c});
        }
        else {
            assert(false);
        }
    }
}
accuracy: 1
time_ms: 110
memory_kb: 39240
final_rank: 5

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

namespace zawa {

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

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

using usize = std::size_t;

} // namespace zawa


namespace zawa {

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

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

} // namespace zawa




namespace zawa {

using Tree = std::vector<std::vector<u32>>;

void AddEdge(Tree& T, u32 u, u32 v) {
    assert(u < T.size());
    assert(v < T.size());
    T[u].emplace_back(v);
    T[v].emplace_back(u);
}

void AddDirectedEdge(Tree& T, u32 from, u32 to) {
    assert(from < T.size());
    assert(to < T.size());
    T[from].emplace_back(to);
}

} // namespace zawa


namespace zawa {

class HeavyLightDecomposition {
public:
    using Vertex = u32;

    static constexpr Vertex Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(Tree T, Vertex root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, Vertex v, Vertex p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (Vertex x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, Vertex v, Vertex idx, Vertex top) -> Vertex {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(Vertex v) const noexcept {
        assert(v < size());
        return size_[v];
    }

    usize depth(Vertex v) const noexcept {
        assert(v < size());
        return dep_[v];
    }

    Vertex parent(Vertex v) const noexcept {
        assert(v < size());
        return par_[v];
    }

    Vertex index(Vertex v) const noexcept {
        assert(v < size());
        return idx_[v];
    }

    Vertex operator[](Vertex v) const noexcept {
        assert(v < size());
        return idx_[v];
    }


    std::vector<std::pair<Vertex, Vertex>> decomp(Vertex s, Vertex t) const {
        assert(s < size());
        assert(t < size());
        std::vector<std::pair<Vertex, Vertex>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<Vertex, Vertex>> operator()(Vertex s, Vertex t) const {
        return decomp(s, t);
    }

    Vertex lca(u32 u, u32 v) const {
        assert(u < size());
        assert(v < size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(Vertex v, Vertex p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    Vertex levelAncestor(Vertex v, usize step) const {
        assert(v < size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    Vertex jump(Vertex s, Vertex t, usize step) const {
        assert(s < size());
        assert(t < size());
        Vertex uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(Vertex s, Vertex t) const {
        assert(s < size());
        assert(t < size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr Vertex INVALID{static_cast<Vertex>(-1)};
    usize n_{};
    std::vector<Vertex> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa


#include <type_traits>

namespace zawa {

template <class Monoid>
class SegmentTree {
public:
    using Value = typename Monoid::Element;
private:
    constexpr u32 left(u32 v) const {
        return v << 1;
    }
    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }
    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize n_;
    std::vector<Value> dat_;

public:
    SegmentTree() = default;
    SegmentTree(u32 n) : n_{ n }, dat_(n << 1, Monoid::identity()) {
        assert(n_);
    }
    SegmentTree(const std::vector<Value>& dat) : n_{ dat.size() }, dat_(dat.size() << 1, Monoid::identity()) {
        assert(n_);
        for (u32 i{} ; i < n_ ; i++) {
            dat_[i + n_] = dat[i];
        }
        for (u32 i{static_cast<u32>(n_) - 1} ; i ; i--) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value get(u32 i) const {
        assert(i < n_);
        return dat_[i + n_];
    }

    void set(u32 i, const Value& value) {
        assert(i < n_);
        i += n_;
        dat_[i] = value;
        while (i = parent(i), i) {
            dat_[i] = Monoid::operation(dat_[left(i)], dat_[right(i)]);
        }
    }

    Value product(u32 l, u32 r) const {
        assert(l < n_);
        assert(l <= r and r <= n_);
        Value leftValue{ Monoid::identity() }, rightValue{ Monoid::identity() };
        for (l += n_, r += n_ ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                leftValue = Monoid::operation(leftValue, dat_[l++]);
            }
            if (r & 1) {
                rightValue = Monoid::operation(dat_[--r], rightValue);
            }
        }
        return Monoid::operation(leftValue, rightValue);
    }

    template <class Function>
    u32 maxRight(u32 l, const Function& f) {
        assert(l < n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "maxRight's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{l}, width{1};
        Value prod{ Monoid::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += n_ ; res + width <= n_ ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(Monoid::operation(prod, dat_[l]))) break; 
            res += width;
            prod = Monoid::operation(prod, dat_[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= n_ and f(Monoid::operation(prod, dat_[l]))) {
                res += width;
                prod = Monoid::operation(prod, dat_[l++]);
            } 
        }
        return res;
    }

    template <class Function>
    u32 minLeft(u32 r, const Function& f) const {
        assert(r <= n_);
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(Value)>>, "minLeft's argument f must be function bool(T)");
        assert(f(Monoid::identity()));
        u32 res{r}, width{1};
        Value prod{ Monoid::identity() };
        for (r += n_ ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(Monoid::operation(dat_[r - 1], prod))) break;
            res -= width;
            prod = Monoid::operation(prod, dat_[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(Monoid::operation(dat_[r - 1], prod))) {
                res -= width;
                prod = Monoid::operation(dat_[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (u32 i{1} ; i < 2 * st.n_ ; i++) {
            os << st.dat_[i] << (i + 1 == 2 * st.n_ ? "" : " ");
        }
        return os;
    }
};

} // namespace zawa



namespace zawa {

// @reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
class Mersenne61ModInt {
public:
    using Value = u64;
private:
    static constexpr Value MOD{(1ull << 61) - 1}; // == MASK61
    static constexpr Value MASK30{(1ull << 30) - 1};
    static constexpr Value MASK31{(1ull << 31) - 1};
    Value v_{};
public:
    constexpr Mersenne61ModInt() {}

    static constexpr Value Mod() noexcept {
        return MOD;
    }
    static constexpr Value Modulo(const Value& v) noexcept {
        Value res{(v >> 61) + (v & MOD)};
        res = (res >= MOD ? res - MOD : res);
        return res;
    }
    static constexpr Value UnsafeMul(const Value& a, const Value& b) noexcept {
        Value fa{a >> 31}, fb{b >> 31};
        Value ba{a & MASK31}, bb{b & MASK31};
        Value mid{fa * bb + fb * ba};
        return Value{2}*fa*fb + (mid >> 30) + ((mid & MASK30) << 31) + ba*bb;
    }
    static constexpr Value Mul(const Value& a, const Value& b) noexcept {
        return Modulo(UnsafeMul(a, b));
    }
};

};


namespace zawa {

struct RollingHashMonoidData {
    using Value = Mersenne61ModInt::Value;
    using Size = usize;
    static Value base;
    Value hash{}, pow{1};
    usize len{};

    constexpr RollingHashMonoidData() = default;
    constexpr RollingHashMonoidData(Value h, Value p, usize l) : hash{h}, pow{p}, len{l} {}
    
    // 単一要素vからなるハッシュを返す
    template <class T>
    static constexpr RollingHashMonoidData generate(const T& v) {
        static_assert(std::is_convertible_v<T, Value>, "v must be convertible unsigned interger value");
        return RollingHashMonoidData{(Value)v, base, 1};
    }

    static Value randomValue(const Value& sigma) {
        return std::mt19937{std::random_device{}()}() % (Mersenne61ModInt::Mod() - sigma) + sigma + 1;
    }

    friend constexpr bool operator==(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash == rhs.hash and lhs.len == rhs.len;
    }
    friend constexpr bool operator!=(const RollingHashMonoidData& lhs, const RollingHashMonoidData& rhs) {
        return lhs.hash != rhs.hash or lhs.len != rhs.len;
    }
};

struct RollingHashMonoid {
    using Modulo = Mersenne61ModInt;
    using Element = RollingHashMonoidData;
    static constexpr Element identity() noexcept {
        return Element{};
    }
    static constexpr Element operation(const Element& lhs, const Element& rhs) noexcept {
        return Element{
            Modulo::Modulo(Modulo::UnsafeMul(lhs.hash, rhs.pow) + rhs.hash),
            Modulo::Mul(lhs.pow, rhs.pow),
            lhs.len + rhs.len
        };
    }
};

} // namespace zawa
using namespace zawa;

using M = RollingHashMonoid;
using E = M::Element;
using V = E::Value;
V E::base{
    E::randomValue(26)
};

int main() {
    SetFastIO();

    int N;
    std::cin >> N;
    std::vector<char> S(N);
    for (int i{} ; i < N ; i++) std::cin >> S[i];
    Tree T(N);
    for (int i{} ; i + 1 < N ; i++) {
        int u, v;
        std::cin >> u >> v;
        AddEdge(T, u - 1, v - 1);
    }
    HeavyLightDecomposition hld(T);
    std::vector<E> cur(N), ruc(N);
    for (int i{} ; i < N ; i++) {
        int p{(int)hld[i]};
        cur[p] = ruc[N - p - 1] = E::generate(S[i]);
    }
    SegmentTree<M> seg(cur), ges(ruc);
    int Q;
    std::cin >> Q;
    std::set<V> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int s, t;
            std::cin >> s >> t;
            s--; t--;
            E hash{M::identity()};
            for (auto [u, v] : hld(s, t)) {
                u = hld[u];
                v = hld[v];
                // std::cout << '(' << u << ',' << v << ')' << ' ';
                if (u <= v) hash = M::operation(hash, seg.product(u, v + 1));
                else hash = M::operation(hash, ges.product(N - u - 1, N - v));
            }
            // std::cout << std::endl;
            set.insert(hash.hash);
            std::cout << set.size() << '\n';
        }
        else if (t == 2) {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            int p{(int)hld[k]};
            cur[p] = ruc[N - p - 1] = E::generate(c);
            seg.set(p, cur[p]);
            ges.set(N - p - 1, ruc[N - p - 1]);
        }
        else {
            assert(false);
        }
    }
}
accuracy: 1
time_ms: 120
memory_kb: 39768
final_rank: 7

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

using namespace std;

#define rep(i,n) for(int i=0;i<(n);++i)
#define reps(i,n) for(int i=1;i<=(n);++i)
#define all(x) begin(x),end(x)
#define Fixed fixed << setprecision(12)
#define updiv(a, b) (((a) + (b) - 1) / (b))
using pii = pair<int,int>;
constexpr int32_t INF  = 0x3f3f3f3f;
constexpr int_fast64_t LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int mod1 = 1e9+7;
constexpr int mod2 = 998244353;

template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}

    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}

template<class T, class U> istream& operator>>(istream& is, pair<T, U>& p){ is >> p.first >> p.second; return (is); }
template<class T, class U> ostream& operator<<(ostream& os, pair<T, U>& p){ os << p.first << ' ' << p.second; return (os); }
template<class T> istream& operator>>(istream& is, vector<T>& v){ for (auto &&e : v) is >> e; return (is); }
template<class T> ostream& operator<<(ostream& os, vector<T>& v){ for (auto &&e : v) os << e << ' '; return (os); }

template<class T> void chbase(vector<T> &v, const T &base) { for (auto &&e : v) e -= base; }
template<class T> inline const T& max(const vector<T> &v) { return *max_element(v.begin(), v.end()); }
template<class T> inline const T& min(const vector<T> &v) { return *min_element(v.begin(), v.end()); }
template <class A, class B> inline bool chmax(A &a, const B &b) { return b > a && (a = b, true); }
template <class A, class B> inline bool chmin(A &a, const B &b) { return b < a && (a = b, true); }

template <class T> using max_heap = priority_queue<T>;
template <class T> using min_heap = priority_queue<T, vector<T>, greater<T> >;
template <class A, class B> using umap = unordered_map<A, B>;
template <class T> using uset = unordered_set<T>;

template <class T> using Max_Heap = __gnu_pbds::priority_queue<T, less<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Min_Heap = __gnu_pbds::priority_queue<T, greater<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Set = __gnu_pbds::tree<T, __gnu_pbds::null_type, less<T>, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
template <class K, class V> using Umap = __gnu_pbds::gp_hash_table<K, V>;
template <class T> using Rope = __gnu_cxx::rope<T>;
using Trie = __gnu_pbds::trie<string, __gnu_pbds::null_type, __gnu_pbds::trie_string_access_traits<>, __gnu_pbds::pat_trie_tag, __gnu_pbds::trie_prefix_search_node_update>;

template <class T> void bye(T x){ cout << x << '\n'; exit(0); }

constexpr int dx[] = {1,0,-1,0,1,1,-1,-1};
constexpr int dy[] = {0,-1,0,1,1,-1,-1,1};

struct RollingHash {
    static const uint64_t mod = (1ull << 61ull) - 1;
    using uint128_t = __uint128_t;
    vector< uint64_t > power;
    const uint64_t base;

    static inline uint64_t add(uint64_t a, uint64_t b) {
        if((a += b) >= mod) a -= mod;
        return a;
    }

    static inline uint64_t mul(uint64_t a, uint64_t b) {
        uint128_t c = (uint128_t) a * b;
        return add(c >> 61, c & mod);
    }

    static inline uint64_t generate_base() {
        mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
        uniform_int_distribution< uint64_t > rand(1, RollingHash::mod - 1);
        return rand(mt);
    }

    inline void expand(size_t sz) {
        if(power.size() < sz + 1) {
            int pre_sz = (int) power.size();
            power.resize(sz + 1);
            for(int i = pre_sz - 1; i < sz; i++) {
                power[i + 1] = mul(power[i], base);
            }
        }
    }

    explicit RollingHash(uint64_t base = generate_base()) : base(base), power{1} {}

    vector< uint64_t > build(const string &s) const {
        int sz = s.size();
        vector< uint64_t > hashed(sz + 1);
        for(int i = 0; i < sz; i++) {
            hashed[i + 1] = add(mul(hashed[i], base), s[i]);
        }
        return hashed;
    }

    template< typename T >
    vector< uint64_t > build(const vector< T > &s) const {
        int sz = s.size();
        vector< uint64_t > hashed(sz + 1);
        for(int i = 0; i < sz; i++) {
            hashed[i + 1] = add(mul(hashed[i], base), s[i]);
        }
        return hashed;
    }

    uint64_t query(const vector< uint64_t > &s, int l, int r) {
        expand(r - l);
        return add(s[r], mod - mul(s[l], power[r - l]));
    }

    uint64_t combine(uint64_t h1, uint64_t h2, size_t h2len) {
        expand(h2len);
        return add(mul(h1, power[h2len]), h2);
    }

    int lcp(const vector< uint64_t > &a, int l1, int r1, const vector< uint64_t > &b, int l2, int r2) {
        int len = min(r1 - l1, r2 - l2);
        int low = 0, high = len + 1;
        while(high - low > 1) {
            int mid = (low + high) / 2;
            if(query(a, l1, l1 + mid) == query(b, l2, l2 + mid)) low = mid;
            else high = mid;
        }
        return low;
    }
};

struct HeavyLightDecomposition {
public:
    vector<vector<int> > g;
    vector<int> sz, in, out, head, rev, par, dep;

    HeavyLightDecomposition(const vector<vector<int> > &G) : g(G) {
        sz.resize(g.size(), 0);
        in.resize(g.size(), 0);
        out.resize(g.size(), 0);
        head.resize(g.size(), 0);
        rev.resize(g.size(), 0);
        par.resize(g.size(), 0);
        dep.resize(g.size(), 0);
        dfs_sz(0, -1, 0);
        dfs_hld(0, -1);
    }

    int la(int v, int k) {
        while (true) {
            int u = head[v];
            if (in[v] - k >= in[u]) return (rev[in[v] - k]);
            k -= in[v] - in[u] + 1;
            v = par[u];
        }
    }

    int lca(int u, int v) const {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) return (u);
        }
    }

    int dist(int u, int v) const {
        return (dep[u] + dep[v] - 2 * dep[lca(u, v)]);
    }

    template<typename E, typename F, typename G, typename S>
    E query(int u, int v, const E &ti, const F &f, const G &g, const S &s, bool edge = false) {
        E l = ti, r = ti;
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v), swap(l, r);
            if (head[u] == head[v]) break;
            l = g(f(in[head[v]], in[v] + 1), l);
        }
        return (s(g(f(in[u] + edge, in[v] + 1), l), r));
    }

    template<typename E, typename F, typename G>
    E query(int u, int v, const E &ti, const F &f, const G &g, bool edge = false) {
        return (query(u, v, ti, f, g, g, edge));
    }

    template<typename G>
    void update(int u, int v, const G &g, bool edge = false) {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) break;
            g(in[head[v]], in[v] + 1);
        }
        g(in[u] + edge, in[v] + 1);
    }

    // (parent, child)
    vector<pair<int, int> > compress(vector<int> &remark) {
        auto comp = [&](int a, int b) { return (in[a] < in[b]); };
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        int K = (int) remark.size();
        for (int k = 1; k < K; ++k) remark.emplace_back(lca(remark[k - 1], remark[k]));
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        vector<pair<int, int> > es;
        vector<int> st;
        for (auto &k : remark) {
            while (!st.empty() && out[st.back()] <= in[k]) st.pop_back();
            if (!st.empty()) es.emplace_back(st.back(), k);
            st.emplace_back(k);
        }
        return (es);
    }

private:
    void dfs_sz(int cur, int p, int d) {
        dep[cur] = d;
        par[cur] = p;
        sz[cur] = 1;
        if (!g[cur].empty() && g[cur][0] == p) swap(g[cur][0], g[cur].back());
        for (auto &to : g[cur]) {
            if (to == p) continue;
            dfs_sz(to, cur, d + 1);
            sz[cur] += sz[to];
            if (sz[g[cur][0]] < sz[to]) swap(g[cur][0], to);
        }
    }

    void dfs_hld(int cur, int p) {
        static int times = 0;
        in[cur] = times++;
        rev[in[cur]] = cur;
        for (auto &to : g[cur]) {
            if (to == p) continue;
            head[to] = (g[cur][0] == to ? head[cur] : to);
            dfs_hld(to, cur);
        }
        out[cur] = times;
    }
};

template< typename T, typename F >
struct SegmentTree {
    int n, sz;
    vector< T > seg;

    const F f;
    const T ti;

    SegmentTree() = default;

    explicit SegmentTree(int n, const F f, const T &ti) : n(n), f(f), ti(ti) {
        sz = 1;
        while(sz < n) sz <<= 1;
        seg.assign(2 * sz, ti);
    }

    explicit SegmentTree(const vector< T > &v, const F f, const T &ti) :
        SegmentTree((int) v.size(), f, ti) {
        build(v);
    }

    void build(const vector< T > &v) {
        assert(n == (int) v.size());
        for(int k = 0; k < n; k++) seg[k + sz] = v[k];
        for(int k = sz - 1; k > 0; k--) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    void update(int k, const T &x) {
        k += sz;
        seg[k] = x;
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    T get(int k) const {
        return seg[k + sz];
    }

    T operator[](const int &k) const {
        return get(k);
    }

    void apply(int k, const T &x) {
        k += sz;
        seg[k] = f(seg[k], x);
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    T query(int l, int r) const {
        T L = ti, R = ti;
        for(l += sz, r += sz; l < r; l >>= 1, r >>= 1) {
            if(l & 1) L = f(L, seg[l++]);
            if(r & 1) R = f(seg[--r], R);
        }
        return f(L, R);
    }

    T query() const {
        return seg[1];
    }

    template< typename C >
    int find_first(int l, const C &check) const {
        if(l >= n) return n;
        l += sz;
        T sum = ti;
        do {
            while((l & 1) == 0) l >>= 1;
            if(check(f(sum, seg[l]))) {
                while(l < sz) {
                    l <<= 1;
                    auto nxt = f(sum, seg[l]);
                    if(not check(nxt)) {
                        sum = nxt;
                        l++;
                    }
                }
                return l + 1 - sz;
            }
            sum = f(sum, seg[l++]);
        } while((l & -l) != l);
        return n;
    }

    template< typename C >
    int find_last(int r, const C &check) const {
        if(r <= 0) return -1;
        r += sz;
        T sum = ti;
        do {
            r--;
            while(r > 1 and (r & 1)) r >>= 1;
            if(check(f(seg[r], sum))) {
                while(r < sz) {
                    r = (r << 1) + 1;
                    auto nxt = f(seg[r], sum);
                    if(not check(nxt)) {
                        sum = nxt;
                        r--;
                    }
                }
                return r - sz;
            }
            sum = f(seg[r], sum);
        } while((r & -r) != r);
        return -1;
    }
};

template< typename T, typename F >
SegmentTree< T, F > get_segment_tree(int N, const F &f, const T &ti) {
    return SegmentTree{N, f, ti};
}

template< typename T, typename F >
SegmentTree< T, F > get_segment_tree(const vector< T > &v, const F &f, const T &ti) {
    return SegmentTree{v, f, ti};
}

signed main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout.setf(ios_base::fixed);
    cout.precision(12);

    int n;
    cin >> n;

    vector<char> k(n);

    cin >> k;

    vector<vector<int> > tree(n);

    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        cin >> u >> v;

        --u, --v;

        tree[u].emplace_back(v);
        tree[v].emplace_back(u);
    }

    HeavyLightDecomposition hld(tree);
    RollingHash rh;
    uset<size_t> st;

    vector<size_t> hs = rh.build("abcdefghijklmnopqrstuvwxyz");
    
    struct Monoid {
        int l, r, len;
        size_t lhs, rhs;
        Monoid(int l = -1, int r = -1, int len = 0, size_t lhs = 0, size_t rhs = 0) : l(l), r(r), len(len), lhs(lhs), rhs(rhs) {}
    };

    auto f = [&](auto x, auto y) {
        int l = x.l == -1 ? y.l : x.l;
        int r = y.r == -1 ? x.r : y.r;
        int len = x.len + y.len;
        size_t lhs = rh.combine(x.lhs, y.lhs, y.len);
        size_t rhs = rh.combine(y.rhs, x.rhs, x.len);
        return Monoid(l, r, len, lhs, rhs);
    };

    auto seg = get_segment_tree(n, f, Monoid(-1, -1, 0, 0, 0));

    for (int i = 0; i < n; ++i) {
        auto h = rh.query(hs, k[i] - 'a', k[i] - 'a' + 1);
        seg.update(hld.in[i], Monoid(i, i, 1, h, h));
    }

    int q;
    cin >> q;

    for (int i = 0; i < q; ++i) {
        int com;
        cin >> com;

        if (com == 1) {
            int s, t;
            cin >> s >> t;
            --s, --t;

            auto res = hld.query(s, t, Monoid(-1, -1, 0, 0, 0), [&](int l, int r) { return seg.query(l, r); }, f, [&](auto x, auto y) {
                swap(x.l, x.r);
                swap(x.lhs, x.rhs);
                return f(x, y);
            });
            if (res.l == s) st.insert(res.lhs);
            else st.insert(res.rhs);

            cout << st.size() << '\n';
        } else {
            int k;
            char c;
            cin >> k >> c;
            --k;

            auto monoid = seg.get(hld.in[k]);
            monoid.lhs = monoid.rhs = rh.query(hs, c - 'a', c - 'a' + 1);
            seg.update(hld.in[k], monoid);
        }
    }


    return (0);
}
accuracy: 1
time_ms: 100
memory_kb: 37396
final_rank: 3

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

using namespace std;

#define rep(i,n) for(int i=0;i<(n);++i)
#define reps(i,n) for(int i=1;i<=(n);++i)
#define all(x) begin(x),end(x)
#define Fixed fixed << setprecision(12)
#define updiv(a, b) (((a) + (b) - 1) / (b))
using pii = pair<int,int>;
constexpr int32_t INF  = 0x3f3f3f3f;
constexpr int_fast64_t LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int mod1 = 1e9+7;
constexpr int mod2 = 998244353;

template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}

    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}

template<class T, class U> istream& operator>>(istream& is, pair<T, U>& p){ is >> p.first >> p.second; return (is); }
template<class T, class U> ostream& operator<<(ostream& os, pair<T, U>& p){ os << p.first << ' ' << p.second; return (os); }
template<class T> istream& operator>>(istream& is, vector<T>& v){ for (auto &&e : v) is >> e; return (is); }
template<class T> ostream& operator<<(ostream& os, vector<T>& v){ for (auto &&e : v) os << e << ' '; return (os); }

template<class T> void chbase(vector<T> &v, const T &base) { for (auto &&e : v) e -= base; }
template<class T> inline const T& max(const vector<T> &v) { return *max_element(v.begin(), v.end()); }
template<class T> inline const T& min(const vector<T> &v) { return *min_element(v.begin(), v.end()); }
template <class A, class B> inline bool chmax(A &a, const B &b) { return b > a && (a = b, true); }
template <class A, class B> inline bool chmin(A &a, const B &b) { return b < a && (a = b, true); }

template <class T> using max_heap = priority_queue<T>;
template <class T> using min_heap = priority_queue<T, vector<T>, greater<T> >;
template <class A, class B> using umap = unordered_map<A, B>;
template <class T> using uset = unordered_set<T>;

template <class T> using Max_Heap = __gnu_pbds::priority_queue<T, less<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Min_Heap = __gnu_pbds::priority_queue<T, greater<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Set = __gnu_pbds::tree<T, __gnu_pbds::null_type, less<T>, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
template <class K, class V> using Umap = __gnu_pbds::gp_hash_table<K, V>;
template <class T> using Rope = __gnu_cxx::rope<T>;
using Trie = __gnu_pbds::trie<string, __gnu_pbds::null_type, __gnu_pbds::trie_string_access_traits<>, __gnu_pbds::pat_trie_tag, __gnu_pbds::trie_prefix_search_node_update>;

template <class T> void bye(T x){ cout << x << '\n'; exit(0); }

constexpr int dx[] = {1,0,-1,0,1,1,-1,-1};
constexpr int dy[] = {0,-1,0,1,1,-1,-1,1};

struct RollingHash {
    static const uint64_t mod = (1ull << 61ull) - 1;
    using uint128_t = __uint128_t;
    vector< uint64_t > power;
    const uint64_t base;

    static inline uint64_t add(uint64_t a, uint64_t b) {
        if((a += b) >= mod) a -= mod;
        return a;
    }

    static inline uint64_t mul(uint64_t a, uint64_t b) {
        uint128_t c = (uint128_t) a * b;
        return add(c >> 61, c & mod);
    }

    static inline uint64_t generate_base() {
        mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
        uniform_int_distribution< uint64_t > rand(1, RollingHash::mod - 1);
        return rand(mt);
    }

    inline void expand(size_t sz) {
        if(power.size() < sz + 1) {
            int pre_sz = (int) power.size();
            power.resize(sz + 1);
            for(int i = pre_sz - 1; i < sz; i++) {
                power[i + 1] = mul(power[i], base);
            }
        }
    }

    explicit RollingHash(uint64_t base = generate_base()) : base(base), power{1} {}

    vector< uint64_t > build(const string &s) const {
        int sz = s.size();
        vector< uint64_t > hashed(sz + 1);
        for(int i = 0; i < sz; i++) {
            hashed[i + 1] = add(mul(hashed[i], base), s[i]);
        }
        return hashed;
    }

    template< typename T >
    vector< uint64_t > build(const vector< T > &s) const {
        int sz = s.size();
        vector< uint64_t > hashed(sz + 1);
        for(int i = 0; i < sz; i++) {
            hashed[i + 1] = add(mul(hashed[i], base), s[i]);
        }
        return hashed;
    }

    uint64_t query(const vector< uint64_t > &s, int l, int r) {
        expand(r - l);
        return add(s[r], mod - mul(s[l], power[r - l]));
    }

    uint64_t combine(uint64_t h1, uint64_t h2, size_t h2len) {
        expand(h2len);
        return add(mul(h1, power[h2len]), h2);
    }

    int lcp(const vector< uint64_t > &a, int l1, int r1, const vector< uint64_t > &b, int l2, int r2) {
        int len = min(r1 - l1, r2 - l2);
        int low = 0, high = len + 1;
        while(high - low > 1) {
            int mid = (low + high) / 2;
            if(query(a, l1, l1 + mid) == query(b, l2, l2 + mid)) low = mid;
            else high = mid;
        }
        return low;
    }
};

struct HeavyLightDecomposition {
public:
    vector<vector<int> > g;
    vector<int> sz, in, out, head, rev, par, dep;

    HeavyLightDecomposition(const vector<vector<int> > &G) : g(G) {
        sz.resize(g.size(), 0);
        in.resize(g.size(), 0);
        out.resize(g.size(), 0);
        head.resize(g.size(), 0);
        rev.resize(g.size(), 0);
        par.resize(g.size(), 0);
        dep.resize(g.size(), 0);
        dfs_sz(0, -1, 0);
        dfs_hld(0, -1);
    }

    int la(int v, int k) {
        while (true) {
            int u = head[v];
            if (in[v] - k >= in[u]) return (rev[in[v] - k]);
            k -= in[v] - in[u] + 1;
            v = par[u];
        }
    }

    int lca(int u, int v) const {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) return (u);
        }
    }

    int dist(int u, int v) const {
        return (dep[u] + dep[v] - 2 * dep[lca(u, v)]);
    }

    template<typename E, typename F, typename G, typename S>
    E query(int u, int v, const E &ti, const F &f, const G &g, const S &s, bool edge = false) {
        E l = ti, r = ti;
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v), swap(l, r);
            if (head[u] == head[v]) break;
            l = g(f(in[head[v]], in[v] + 1), l);
        }
        return (s(g(f(in[u] + edge, in[v] + 1), l), r));
    }

    template<typename E, typename F, typename G>
    E query(int u, int v, const E &ti, const F &f, const G &g, bool edge = false) {
        return (query(u, v, ti, f, g, g, edge));
    }

    template<typename G>
    void update(int u, int v, const G &g, bool edge = false) {
        for (;; v = par[head[v]]) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) break;
            g(in[head[v]], in[v] + 1);
        }
        g(in[u] + edge, in[v] + 1);
    }

    // (parent, child)
    vector<pair<int, int> > compress(vector<int> &remark) {
        auto comp = [&](int a, int b) { return (in[a] < in[b]); };
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        int K = (int) remark.size();
        for (int k = 1; k < K; ++k) remark.emplace_back(lca(remark[k - 1], remark[k]));
        sort(begin(remark), end(remark), comp);
        remark.erase(unique(begin(remark), end(remark)), end(remark));
        vector<pair<int, int> > es;
        vector<int> st;
        for (auto &k : remark) {
            while (!st.empty() && out[st.back()] <= in[k]) st.pop_back();
            if (!st.empty()) es.emplace_back(st.back(), k);
            st.emplace_back(k);
        }
        return (es);
    }

private:
    void dfs_sz(int cur, int p, int d) {
        dep[cur] = d;
        par[cur] = p;
        sz[cur] = 1;
        if (!g[cur].empty() && g[cur][0] == p) swap(g[cur][0], g[cur].back());
        for (auto &to : g[cur]) {
            if (to == p) continue;
            dfs_sz(to, cur, d + 1);
            sz[cur] += sz[to];
            if (sz[g[cur][0]] < sz[to]) swap(g[cur][0], to);
        }
    }

    void dfs_hld(int cur, int p) {
        static int times = 0;
        in[cur] = times++;
        rev[in[cur]] = cur;
        for (auto &to : g[cur]) {
            if (to == p) continue;
            head[to] = (g[cur][0] == to ? head[cur] : to);
            dfs_hld(to, cur);
        }
        out[cur] = times;
    }
};

template< typename T, typename F >
struct SegmentTree {
    int n, sz;
    vector< T > seg;

    const F f;
    const T ti;

    SegmentTree() = default;

    explicit SegmentTree(int n, const F f, const T &ti) : n(n), f(f), ti(ti) {
        sz = 1;
        while(sz < n) sz <<= 1;
        seg.assign(2 * sz, ti);
    }

    explicit SegmentTree(const vector< T > &v, const F f, const T &ti) :
        SegmentTree((int) v.size(), f, ti) {
        build(v);
    }

    void build(const vector< T > &v) {
        assert(n == (int) v.size());
        for(int k = 0; k < n; k++) seg[k + sz] = v[k];
        for(int k = sz - 1; k > 0; k--) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    void update(int k, const T &x) {
        k += sz;
        seg[k] = x;
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    T get(int k) const {
        return seg[k + sz];
    }

    T operator[](const int &k) const {
        return get(k);
    }

    void apply(int k, const T &x) {
        k += sz;
        seg[k] = f(seg[k], x);
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    T query(int l, int r) const {
        T L = ti, R = ti;
        for(l += sz, r += sz; l < r; l >>= 1, r >>= 1) {
            if(l & 1) L = f(L, seg[l++]);
            if(r & 1) R = f(seg[--r], R);
        }
        return f(L, R);
    }

    T query() const {
        return seg[1];
    }

    template< typename C >
    int find_first(int l, const C &check) const {
        if(l >= n) return n;
        l += sz;
        T sum = ti;
        do {
            while((l & 1) == 0) l >>= 1;
            if(check(f(sum, seg[l]))) {
                while(l < sz) {
                    l <<= 1;
                    auto nxt = f(sum, seg[l]);
                    if(not check(nxt)) {
                        sum = nxt;
                        l++;
                    }
                }
                return l + 1 - sz;
            }
            sum = f(sum, seg[l++]);
        } while((l & -l) != l);
        return n;
    }

    template< typename C >
    int find_last(int r, const C &check) const {
        if(r <= 0) return -1;
        r += sz;
        T sum = ti;
        do {
            r--;
            while(r > 1 and (r & 1)) r >>= 1;
            if(check(f(seg[r], sum))) {
                while(r < sz) {
                    r = (r << 1) + 1;
                    auto nxt = f(seg[r], sum);
                    if(not check(nxt)) {
                        sum = nxt;
                        r--;
                    }
                }
                return r - sz;
            }
            sum = f(seg[r], sum);
        } while((r & -r) != r);
        return -1;
    }
};

template< typename T, typename F >
SegmentTree< T, F > get_segment_tree(int N, const F &f, const T &ti) {
    return SegmentTree{N, f, ti};
}

template< typename T, typename F >
SegmentTree< T, F > get_segment_tree(const vector< T > &v, const F &f, const T &ti) {
    return SegmentTree{v, f, ti};
}

signed main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout.setf(ios_base::fixed);
    cout.precision(12);

    int n;
    cin >> n;

    vector<char> k(n);

    cin >> k;

    vector<vector<int> > tree(n);

    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        cin >> u >> v;

        --u, --v;

        tree[u].emplace_back(v);
        tree[v].emplace_back(u);
    }

    HeavyLightDecomposition hld(tree);
    RollingHash rh;
    uset<size_t> st;

    string abc = "abcdefghijklmnopqrstuvwxyz";
    vector<size_t> hs = rh.build(abc);
    
    struct Monoid {
        int l, r, len;
        size_t lhs, rhs;
        Monoid(int l = -1, int r = -1, int len = 0, size_t lhs = 0, size_t rhs = 0) : l(l), r(r), len(len), lhs(lhs), rhs(rhs) {}
    };

    auto f = [&](auto x, auto y) {
        int l = x.l == -1 ? y.l : x.l;
        int r = y.r == -1 ? x.r : y.r;
        int len = x.len + y.len;
        size_t lhs = x.l == -1 ? y.lhs : y.l == -1 ? x.lhs : rh.combine(x.lhs, y.lhs, y.len);
        // size_t lhs = x.l == -1 ? y.lhs : rh.combine(x.lhs, y.lhs, y.len);
        size_t rhs = y.l == -1 ? x.rhs : x.l == -1 ? y.rhs : rh.combine(y.rhs, x.rhs, x.len);
        // size_t rhs = y.l == -1 ? x.rhs : rh.combine(y.rhs, x.rhs, x.len);
        return Monoid(l, r, len, lhs, rhs);
    };

    auto seg = get_segment_tree(n, f, Monoid(-1, -1, 0, 0, 0));

    for (int i = 0; i < n; ++i) {
        auto h = rh.query(hs, k[i] - 'a', k[i] - 'a' + 1);
        seg.update(hld.in[i], Monoid(i, i, 1, h, h));
    }

    int q;
    cin >> q;

    for (int i = 0; i < q; ++i) {
        int com;
        cin >> com;

        if (com == 1) {
            int s, t;
            cin >> s >> t;
            --s, --t;

            auto res = hld.query(s, t, Monoid(-1, -1, 0, 0, 0), [&](int l, int r) { return seg.query(l, r); }, f, [&](auto x, auto y) {
                swap(x.l, x.r);
                swap(x.lhs, x.rhs);
                return f(x, y);
            });
            if (res.l == s) st.insert(res.lhs);
            else st.insert(res.rhs);

            cout << st.size() << '\n';
        } else {
            int k;
            char c;
            cin >> k >> c;
            --k;

            auto monoid = seg.get(hld.in[k]);
            monoid.lhs = monoid.rhs = rh.query(hs, c - 'a', c - 'a' + 1);
            seg.update(hld.in[k], monoid);
        }
    }


    return (0);
}
accuracy: 1
time_ms: 100
memory_kb: 37416
final_rank: 4

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

template <class T>
bool chmin(T& lhs, T rhs) {
    if (lhs > rhs) {
        lhs = rhs;
        return true;
    }
    return false;
}

template <class T>
bool chmax(T& lhs, T rhs) {
    if (lhs < rhs) {
        lhs = rhs;
        return true;
    }
    return false;
}

constexpr long long MOD = 998244353;

struct Data {
    long long coef;
    long long constant;
    Data(long long a = 1, long long b = 0): coef((a % MOD + MOD) % MOD), constant((b % MOD + MOD) % MOD) {}
    long long eval() {
        return constant;
    }
};

Data op(Data lhs, Data rhs) {
    return Data(lhs.coef * rhs.coef, lhs.coef * rhs.constant + lhs.constant);
}

struct Hash {
    static constexpr int B = 2;
    static constexpr long long BASE[B] = {12045, 13501};
    static constexpr long long BASE_INV[B] = {456067968, 364295232};
    std::array<Data, B> ord, rev;
    Hash(std::array<Data, B> a, std::array<Data, B> b): ord(a), rev(b) {}
    Hash(long long val) {
        ord = {Data(BASE[0], val), Data(BASE[1], val)};
        rev = {Data(BASE[0], val), Data(BASE[1], val)};
    }
    long long eval() {
        long long lhs = ord[0].eval();
        long long rhs = ord[1].eval();
        return lhs * MOD + rhs;
    }
};

Hash e() {
    return Hash({Data(), Data()}, {Data(), Data()});
}

Hash op(Hash lhs, Hash rhs) {
    Hash hash = e();
    for (int i = 0; i < Hash::B; i++) {
        hash.ord[i] = op(lhs.ord[i], rhs.ord[i]);
    }
    for (int i = 0; i < Hash::B; i++) {
        hash.rev[i] = op(rhs.rev[i], lhs.rev[i]);
    }
    return hash;
}

Hash reverse(Hash hash) {
    std::swap(hash.ord, hash.rev);
    return hash;
}

template <class T, T (*op)(T, T), T (*e)()>
struct SegmentTree {
    int n;
    std::vector<T> data;
    SegmentTree(int n): n(n), data(2 * n, e()) {}
    void set(int pos, T elem) {
        assert(0 <= pos && pos < n);
        pos += n;
        data[pos] = elem;
        for (pos >>= 1; pos > 0; pos >>= 1) {
            data[pos] = op(data[2 * pos], data[2 * pos + 1]);
        }
    }
    T prod(int left, int right) {
        assert(0 <= left && left <= n);
        assert(left <= right && right <= n);
        T lhs = e();
        T rhs = e();
        for (left += n, right += n; left < right; left >>= 1, right >>= 1) {
            if (left & 1) lhs = op(lhs, data[left++]);
            if (right & 1) rhs = op(data[--right], rhs);
        }
        return op(lhs, rhs);
    }
};

const int ROOT = 0;

struct HLD {
    std::vector<int> head;
    std::vector<int> in;
    std::vector<int> out;
    std::vector<int> parent;
    std::vector<int> depth;

    HLD(std::vector<std::vector<int>> graph) {
        int n = graph.size();
        {
            std::vector<int> longest_path(n);
            auto dfs = [&](auto self, int u, int p) -> int {
                int longest_path_id = 0;
                for (int i = 0; i < (int)graph[u].size(); i++) {
                    int v = graph[u][i];
                    if (v == p) continue;
                    if (chmax(longest_path[u], self(self, v, u) + 1)) {
                        longest_path_id = i;
                    }
                }
                if (longest_path_id != 0) {
                    std::swap(graph[u][longest_path_id], graph[u][0]);
                }
                return longest_path[u];
            };
            dfs(dfs, ROOT, ROOT);
        }

        head.assign(n, -1);
        in.assign(n, 0);
        out.assign(n, 0);
        parent.assign(n, 0);
        depth.assign(n, 0);
        {
            int now = 0;
            auto dfs = [&](auto self, int u, int p) -> void {
                if (head[u] == -1) head[u] = u;
                parent[u] = p;
                in[u] = now++;
                for (int i = 0; i < (int)graph[u].size(); i++) {
                    int v = graph[u][i];
                    if (v == p) continue;
                    if (i == 0) head[v] = head[u];
                    depth[v] = depth[u] + 1;
                    self(self, v, u);
                }
                out[u] = now;
            };
            dfs(dfs, ROOT, ROOT);
        }
    }
    int lca(int lhs, int rhs) {
        if (depth[head[lhs]] < depth[head[rhs]]) std::swap(lhs, rhs);
        while (head[lhs] != head[rhs]) {
            lhs = parent[head[lhs]];
            if (depth[head[lhs]] < depth[head[rhs]]) std::swap(lhs, rhs);
        }
        if (depth[lhs] < depth[rhs]) return lhs;
        else return rhs;
    }
    using Interval = std::pair<int, int>;
    using Intervals = std::vector<Interval>;

    // for vertex
    int vertex(int pos) {
        return in[pos];
    }
    Interval subtree(int subroot) {
        return {in[subroot], out[subroot]};
    }
    std::pair<Intervals, Intervals> path(int lhs, int rhs) {
        int subroot = lca(lhs, rhs);
        auto lca_to_lhs = from_subroot(subroot, lhs);
        auto lca_to_rhs = from_subroot(subroot, rhs);
        lca_to_lhs.insert(lca_to_lhs.begin(), {in[subroot], in[subroot] + 1});
        return {lca_to_lhs, lca_to_rhs};
    }
    //  lhs -> ... -> rhs
    //  lhsを含まない
    Intervals from_subroot(int lhs, int rhs) {
        Intervals intervals;
        assert(depth[lhs] <= depth[rhs]);
        while (head[lhs] != head[rhs]) {
            intervals.emplace_back(in[head[rhs]], in[rhs] + 1);
            rhs = parent[head[rhs]];
        }
        intervals.emplace_back(in[lhs] + 1, in[rhs] + 1);
        std::reverse(intervals.begin(), intervals.end());
        return intervals;
    }
};

int main() {
    int n;
    std::cin >> n;
    std::vector<char> vertex(n);
    for (int i = 0; i < n; i++) std::cin >> vertex[i];
    std::vector graph(n, std::vector<int>());
    for (int i = 0; i + 1 < n; i++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        graph[u].push_back(v);
        graph[v].push_back(u);
    }
    HLD hld(graph);
    SegmentTree<Hash, op, e> segtree(n);
    for (int i = 0; i < n; i++) {
        segtree.set(hld.vertex(i), Hash(vertex[i]));
    }

    int q;
    std::cin >> q;
    std::set<long long> str;
    while (q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int u, v;
            std::cin >> u >> v;
            u--; v--;
            auto [lhs, rhs] = hld.path(u, v);
            Hash lca_to_u = e();
            for (auto [l, r]: lhs) {
                lca_to_u = op(lca_to_u, segtree.prod(l, r));
            }
            Hash lca_to_v = e();
            for (auto [l, r]: rhs) {
                lca_to_v = op(lca_to_v, segtree.prod(l, r));
            }
            auto hash = op(reverse(lca_to_u), lca_to_v);
            str.insert(hash.eval());
            std::cout << str.size() << '\n';
        } else {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            segtree.set(hld.vertex(k), Hash(c));
        }
    }
}
accuracy: 1
time_ms: 270
memory_kb: 38724
final_rank: 13

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

constexpr long long MOD = 998244353;

struct Data {
    long long coef;
    long long constant;
    Data(long long a = 1, long long b = 0): coef((a % MOD + MOD) % MOD), constant((b % MOD + MOD) % MOD) {}
    long long eval() {
        return constant;
    }
};

// Data e() {
//     return Data(0, 0);
// }

Data op(Data lhs, Data rhs) {
    return Data(lhs.coef * rhs.coef, lhs.coef * rhs.constant + lhs.constant);
}

struct Hash {
    static constexpr int B = 2;
    static constexpr long long BASE[B] = {12045, 13501};
    static constexpr long long BASE_INV[B] = {456067968, 364295232};
    std::array<Data, B> ord, rev;
    Hash(std::array<Data, B> a, std::array<Data, B> b): ord(a), rev(b) {}
    Hash(long long val) {
        if (val >= 0) {
            ord = {Data(BASE[0], val), Data(BASE[1], val)};
            rev = {Data(BASE[0], val), Data(BASE[1], val)};
        } else {
            ord = {Data(BASE_INV[0], (val + MOD) * BASE_INV[0]), Data(BASE_INV[1], (val + MOD) * BASE_INV[1])};
            rev = {Data(BASE_INV[0], (val + MOD) * BASE_INV[0]), Data(BASE_INV[1], (val + MOD) * BASE_INV[1])};
        }
    }
    long long eval() {
        long long lhs = ord[0].eval();
        long long rhs = ord[1].eval();
        return lhs * MOD + rhs;
    }
};

Hash e() {
    return Hash({Data(), Data()}, {Data(), Data()});
}

Hash op(Hash lhs, Hash rhs) {
    Hash hash = e();
    for (int i = 0; i < Hash::B; i++) {
        hash.ord[i] = op(lhs.ord[i], rhs.ord[i]);
    }
    for (int i = 0; i < Hash::B; i++) {
        hash.rev[i] = op(rhs.rev[i], lhs.rev[i]);
    }
    return hash;
}

Hash reverse(Hash hash) {
    std::swap(hash.ord, hash.rev);
    return hash;
}

template <class T, T (*op)(T, T), T (*e)()>
struct SegmentTree {
    int n;
    std::vector<T> data;
    SegmentTree(int n): n(n), data(2 * n, e()) {}
    void set(int pos, T elem) {
        pos += n;
        data[pos] = elem;
        for (pos >>= 1; pos > 0; pos >>= 1) {
            data[pos] = op(data[2 * pos], data[2 * pos + 1]);
        }
    }
    T prod(int left, int right) {
        T lhs = e();
        T rhs = e();
        for (left += n, right += n; left < right; left >>= 1, right >>= 1) {
            if (left & 1) lhs = op(lhs, data[left++]);
            if (right & 1) rhs = op(data[--right], rhs);
        }
        return op(lhs, rhs);
    }
};

const int ROOT = 0;

struct LCA {
    static constexpr int LOG = 20;
    std::vector<int> depth;
    std::array<std::vector<int>, LOG + 1> data;
    LCA(const std::vector<std::vector<int>>& graph) {
        int n = graph.size();
        depth.assign(n, 0);
        for (int i = 0; i <= LOG; i++) data[i].assign(n, ROOT);
        auto dfs = [&](auto self, int u, int p) -> void {
            data[0][u] = p;
            for (auto v: graph[u]) {
                if (v == p) continue;
                depth[v] = depth[u] + 1;
                self(self, v, u);
            }
        };
        dfs(dfs, ROOT, ROOT);
        for (int i = 0; i < LOG; i++) {
            for (int j = 0; j < n; j++) {
                data[i + 1][j] = data[i][data[i][j]];
            }
        }
    }
    int get(int lhs, int rhs) {
        if (depth[lhs] < depth[rhs]) std::swap(lhs, rhs);
        int diff = depth[lhs] - depth[rhs];
        for (int i = 0; i < LOG; i++) {
            if (diff & (1 << i)) {
                lhs = data[i][lhs];
            }
        }
        assert(depth[lhs] == depth[rhs]);
        if (lhs == rhs) return lhs;
        for (int i = LOG; i >= 0; i--) {
            if (data[i][lhs] != data[i][rhs]) {
                lhs = data[i][lhs];
                rhs = data[i][rhs];
            }
        }
        assert(lhs != rhs);
        assert(data[0][lhs] == data[0][rhs]);
        return data[0][lhs];
    }
    int ancester(int u, int v) {
        if (u == v) return v;
        int diff = depth[v] - depth[u] - 1;
        for (int i = 0; i < LOG; i++) {
            if (diff & (1 << i)) {
                v = data[i][v];
            }
        }
        assert(data[0][v] == u);
        return v;
    }
};

int main() {
    int n;
    std::cin >> n;
    std::vector<char> vertex(n);
    for (int i = 0; i < n; i++) std::cin >> vertex[i];
    std::vector graph(n, std::vector<int>());
    for (int i = 0; i + 1 < n; i++) {
        int u, v;
        std::cin >> u >> v;
        u--; v--;
        graph[u].push_back(v);
        graph[v].push_back(u);
    }
    std::vector<int> in(n), out(n);
    {
        int now = 0;
        auto dfs = [&](auto self, int u, int p) -> void {
            in[u] = now++;
            for (auto v: graph[u]) {
                if (v == p) continue;
                self(self, v, u);
            }
            out[u] = now++;
        };
        dfs(dfs, ROOT, ROOT);
        assert(2 * n == now);
    }
    LCA lca(graph);

    SegmentTree<Hash, op, e> segtree(2 * n);
    // auto set = [&](int pos, long long val) {
    //     Data data(val);
    //     segtree.set(pos, Hash({data, data}, {data, data}));
    // };

    for (int i = 0; i < n; i++) {
        segtree.set(in[i], Hash((int)vertex[i]));
        segtree.set(out[i], Hash(-1 * (int)vertex[i]));
    }

    int q;
    std::cin >> q;
    std::set<long long> str;
    while (q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int u, v;
            std::cin >> u >> v;
            u--; v--;
            int w = lca.get(u, v);
            if (u == w || v == w) {
                Hash hash = e();
                if (u == w) {
                    hash = segtree.prod(in[w], out[v]);
                } else {
                    hash = segtree.prod(in[w], out[u]);
                    hash = reverse(hash);
                }
                str.insert(hash.eval());
            } else {
                Hash w_to_u = segtree.prod(in[lca.ancester(w, u)], out[u]);
                Hash w_to_v = segtree.prod(in[lca.ancester(w, v)], out[v]);
                auto hash = op(op(reverse(w_to_u), Hash(vertex[w])), w_to_v);
                str.insert(hash.eval());
            }
            std::cout << str.size() << '\n';
        } else {
            int k;
            char c;
            std::cin >> k >> c;
            k--;
            segtree.set(in[k], Hash((int)c));
            segtree.set(out[k], Hash(-1 * (int)c));
            vertex[k] = c;
        }
    }
}
accuracy: 1
time_ms: 380
memory_kb: 57084
final_rank: 15

code_snippet: #include"bits/stdc++.h"
#define overload(a,b,c,d,...) d
#define rep1(n) for(ll _=0;_<(ll)n;++_)
#define rep2(i,n) for(ll i=0;i<(ll)n;++i)
#define rep3(i,a,b) for(ll i=(ll)a;i<(ll)b;++i)
#define rep(...) overload(__VA_ARGS__,rep3,rep2,rep1)(__VA_ARGS__)
#define rrep1(i,n) for(ll i=(ll)n-1;i>=0;--i)
#define rrep2(i,a,b) for(ll i=(ll)b-1;i>=(ll)a;--i)
#define rrep(...) overload(__VA_ARGS__,rrep2,rrep1)(__VA_ARGS__)
#define all(x) (x).begin(),(x).end()
#define rall(x) (x).rbegin(),(x).rend()
#define eb emplace_back
#define pb push_back
using namespace std;
using ll=long long;
using ull=unsigned long long;
using ld=long double;
using vl=vector<ll>;
template<typename T,typename U>inline constexpr bool chmin(T&a,const U&b){return (a>b?a=b,true:false);}
template<typename T,typename U>inline constexpr bool chmax(T&a,const U&b){return (a<b?a=b,true:false);}
static constexpr ll inf=numeric_limits<ll>::max()/2;
static constexpr ull mod=(1ull<<61)-1;
static const ull base=random_device()()%mod;
ull calc_mod(ull a){
	ull ret=(a&mod)+(a>>61);
	if(ret>=mod)ret-=mod;
	return ret;
}
ull add(ull a,ull b){
	ull ret=a+b;
	if(ret>=mod)ret-=mod;
	return ret;
}
ull mul(ull a,ull b){
	__uint128_t c=(__uint128_t)a*b;
	return add(c&mod,c>>61);
}
struct Str{
	private:
	ull hash,bs;
	public:
	Str():hash(0),bs(1){}
	Str(char c):hash(c),bs(base){}
	ull get_hash()const{return hash;}
	friend Str operator+(const Str&lhs,const Str&rhs){
		Str ret;
		ret.hash=add(mul(rhs.bs,lhs.hash),rhs.hash);
		ret.bs=mul(rhs.bs,lhs.bs);
		return ret;
	}
};
struct segtree{
	private:
	ll sz;
	vector<Str>seg;
	public:
	segtree():segtree(0){}
	segtree(ll n){
		sz=1;
		while(sz<n)sz<<=1;
		seg.resize(sz<<1);
	}
	segtree(const vector<Str>&v){
		const int n=v.size();
		sz=1;
		while(sz<n)sz<<=1;
		seg.resize(sz<<1);
		rep(i,n)seg[sz+i]=v[i];
		rrep(i,sz)seg[i]=seg[i<<1]+seg[i<<1|1];
	}
	Str query(int l,int r)const{
		l+=sz,r+=sz;
		Str lhs,rhs;
		while(l!=r){
			if(l&1)lhs=lhs+seg[l++];
			if(r&1)rhs=seg[--r]+rhs;
			l>>=1,r>>=1;
		}
		return lhs+rhs;
	}
	void update(int i,const Str&v){
		i+=sz;
		seg[i]=v;
		while(i>>=1){
			seg[i]=seg[i<<1]+seg[i<<1|1];
		}
	}
};
struct HLD{
	private:
	int n,root,cnt;
	vector<int>subsz,head,in,out,par;
	vector<vector<int>>g;
	vector<vector<pair<ll,ll>>>st;
	int szdfs(int u,int p=-1){
		subsz[u]=1;
		for(auto&&e:g[u]){
			if(e==p)continue;
			subsz[u]+=szdfs(e,u);
		}
		return subsz[u];
	}
	void bldfs(int u,int p=-1){
		par[u]=p;
		in[u]=cnt++;
		int idx=-1;
		for(auto&&e:g[u]){
			if(e==p)continue;
			if(idx==-1||subsz[idx]<subsz[e])idx=e;
		}
		if(idx!=-1){
			head[idx]=head[u];
			bldfs(idx,u);
		}
		for(auto&&e:g[u]){
			if(e==idx||e==p)continue;
			head[e]=e;
			bldfs(e,u);
		}
		out[u]=cnt;
	}
	void init(){
		n=g.size();
		subsz.assign(n,-1);
		szdfs(root);
		cnt=0;
		head.assign(n,-1);
		head[root]=root;
		in.resize(n);
		out.resize(n);
		par.resize(n);
		st.resize(n);
		bldfs(root,-1);
	}
	public:
	HLD(const vector<vector<int>>&g,int root=0):root(root),g(g){
		init();
	}
	int lca(int u,int v)const{
		while(head[u]!=head[v]){
			if(in[u]>in[v])swap(u,v);
			v=par[head[v]];
		}
		return in[u]<in[v]?u:v;
	}
	vector<pair<int,int>>up_path(int u,int v){
		vector<pair<int,int>>ret;
		while(head[u]!=head[v]){
			ret.emplace_back(in[u],in[head[u]]);
			u=par[head[u]];
		}
		if(u!=v)ret.emplace_back(in[u],in[v]+1);
		return ret;
	}
	vector<pair<int,int>>down_path(int u,int v){
		vector<pair<int,int>>ret;
		while(head[u]!=head[v]){
			ret.emplace_back(in[head[v]],in[v]);
			v=par[head[v]];
		}
		ret.emplace_back(in[u],in[v]);
		reverse(all(ret));
		return ret;
	}
	int get_id(int v)const{return in[v];}
};
int main(){
	int n;cin>>n;
	vector<Str>s(n);
	rep(i,n){
		char c;cin>>c;
		s[i]=Str(c);
	}
	vector<vector<int>>g(n);
	rep(i,n-1){
		int u,v;cin>>u>>v;u--,v--;
		g[u].emplace_back(v);
		g[v].emplace_back(u);
	}
	HLD hld(g);
	vector<Str>t(n);
	rep(i,n)t[hld.get_id(i)]=s[i];
	segtree seg1(t);
	reverse(all(t));
	segtree seg2(t);
	int q;cin>>q;
	set<ull>st;
	auto f=[&](int l,int r)->Str {
		if(l>=r){
			return seg2.query(n-1-l,n-r);
		}
		return seg1.query(l,r+1);
	};
	while(q--){
		int t;cin>>t;
		//cerr<<"t:"<<t<<endl;
		if(t==1){
			int l,r;cin>>l>>r;l--,r--;
			//cerr<<l<<r<<endl;
			const int lc=hld.lca(l,r);
			auto a=hld.up_path(l,lc);
			auto b=hld.down_path(lc,r);
			Str res;
			for(auto [idl,idr]:a)res=res+f(idl,idr);
			for(auto [idl,idr]:b)res=res+f(idl,idr);
			st.emplace(res.get_hash());
			cout<<st.size()<<endl;
		}
		else{
			int k;cin>>k;k--;
			char c;cin>>c;
			s[k]=Str(c);
			seg1.update(hld.get_id(k),s[k]);
			seg2.update(n-1-hld.get_id(k),s[k]);
		}
	}
}
accuracy: 1
time_ms: 180
memory_kb: 42208
final_rank: 11

code_snippet: /* -*- coding: utf-8 -*-
 *
 * 0478.cc: Tree Keyboard
 */

#include<cstdio>
#include<vector>
#include<set>
#include<algorithm>
 
using namespace std;

/* constant */

const int MAX_N = 100000;
const int BN = 17;
const int P = 40063;
const int MOD = 1000000021;

/* typedef */

typedef vector<int> vi;
typedef set<int> si;

template<const int MOD>
struct MI {
  int v;
  MI(): v() {}
  MI(int _v): v(_v % MOD) {}
  MI(long long _v): v(_v % MOD) {}

  MI operator+(const MI m) const { return MI(v + m.v); }
  MI operator-(const MI m) const { return MI(v + MOD - m.v); }
  MI operator*(const MI m) const { return MI((long long)v * m.v); }
  MI operator-() const { return MI(MOD - v); }

  MI &operator+=(const MI m) { return (*this = *this + m); }
  MI &operator-=(const MI m) { return (*this = *this - m); }
  MI &operator*=(const MI m) { return (*this = *this * m); }

  bool operator==(const MI m) const { return v == m.v; }
  bool operator!=(const MI m) const { return v != m.v; }

  MI pow(int n) const {  // a^n % MOD
    MI pm = 1, a = *this;
    while (n > 0) {
      if (n & 1) pm *= a;
      a *= a;
      n >>= 1;
    }
    return pm;
  }

  MI inv() const { return pow(MOD - 2); }
  MI operator/(const MI m) const { return *this * m.inv(); }
  MI &operator/=(const MI m) { return (*this = *this / m); }
};

typedef MI<MOD> mi;

template <typename T>
struct SegTreeSum {
  int e2;
  vector<T> nodes;
  T defv;
  SegTreeSum() {}

  void init(int n, T _defv) {
    defv = _defv;
    for (e2 = 1; e2 < n; e2 <<= 1);
    nodes.assign(e2 * 2, defv);
  }

  T &geti(int i) { return nodes[e2 - 1 + i]; }
  void seti(int i, T v) { geti(i) = v; }

  void setall() {
    for (int j = e2 - 2; j >= 0; j--)
      nodes[j] = nodes[j * 2 + 1] + nodes[j * 2 + 2];
  }

  void set(int i, T v) {
    int j = e2 - 1 + i;
    nodes[j] = v;
    while (j > 0) {
      j = (j - 1) / 2;
      nodes[j] = nodes[j * 2 + 1] + nodes[j * 2 + 2];
    }
  }

  T sum_range(int r0, int r1, int k, int i0, int i1) {
    if (r1 <= i0 || i1 <= r0) return defv;
    if (r0 <= i0 && i1 <= r1) return nodes[k];

    int im = (i0 + i1) / 2;
    T v0 = sum_range(r0, r1, k * 2 + 1, i0, im);
    T v1 = sum_range(r0, r1, k * 2 + 2, im, i1);
    return v0 + v1;
  }
  T sum_range(int r0, int r1) { return sum_range(r0, r1, 0, 0, e2); }
};

/* global variables */

char ks[MAX_N];
vi nbrs[MAX_N];
int ps[MAX_N][BN], ds[MAX_N], cis[MAX_N], ls[MAX_N], rs[MAX_N];
mi pes[MAX_N + 1], invpes[MAX_N + 1];
SegTreeSum<mi> st0, st1;
si hs[MAX_N + 1];

/* subroutines */

inline void prep_rhash(int n) {
  pes[0] = invpes[0] = 1;
  pes[1] = P;
  invpes[1] = pes[1].inv();
  for (int i = 2; i <= n; i++) {
    pes[i] = pes[i - 1] * P;
    invpes[i] = invpes[i - 1] * invpes[1];
  }
}

inline int ancestor(int u, int d) {
  for (int i = BN - 1; i >= 0; i--)
    if ((d >> i) & 1) u = ps[u][i];
  return u;
}

int find_lca(int u, int v) {
  if (ds[u] > ds[v]) swap(u, v);

  v = ancestor(v, ds[v] - ds[u]);
  if (u == v) return u;

  for(int i = BN - 1; i >= 0; i--)
    if (ps[u][i] != ps[v][i])
      u = ps[u][i], v = ps[v][i];
  return ps[u][0];
}

/* main */

int main() {
  int n;
  scanf("%d", &n);

  for (int i = 0; i < n; i++) {
    char s[4];
    scanf("%s", s);
    ks[i] = s[0];
  }

  for (int i = 1; i < n; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    u--, v--;
    nbrs[u].push_back(v);
    nbrs[v].push_back(u);
  }

  int maxd = 0;
  ps[0][0] = -1, ds[0] = 0;
  for (int u = 0, l = 0; u >= 0;) {
    vi &nbru = nbrs[u];
    int up = ps[u][0];

    if (cis[u] == 0) ls[u] = l++;
    if (cis[u] < nbru.size()) {
      int v = nbru[cis[u]++];
      if (v != up) {
	ps[v][0] = u, ds[v] = ds[u] + 1;
	maxd = max(maxd, ds[v]);
	u = v;
      }
    }
    else {
      rs[u] = l++;
      u = up;
    }
  }
  //printf("maxd=%d\n", maxd);

  for (int i = 0; i < BN - 1; i++)
    for (int u = 0; u < n; u++)
      ps[u][i + 1] = (ps[u][i] >= 0) ? ps[ps[u][i]][i] : -1;

  prep_rhash(n);
  st0.init(n * 2, 0);
  st1.init(n * 2, 0);

  for (int u = 0; u < n; u++) {
    mi pk0 = pes[maxd - ds[u]] * ks[u];
    mi pk1 = pes[ds[u]] * ks[u];
    st0.seti(ls[u], pk0);
    st0.seti(rs[u], -pk0);
    st1.seti(ls[u], pk1);
    st1.seti(rs[u], -pk1);
  }
  st0.setall();
  st1.setall();

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

  for (int cnt = 0; qn--;) {
    int op;
    scanf("%d", &op);

    if (op == 1) {
      int s, t;
      scanf("%d%d", &s, &t);
      s--, t--;

      int w = find_lca(s, t);
      //printf("s=%d, t=%d, w=%d\n", s, t, w);

      int v, l;
      if (w == s) {
	mi e = st1.sum_range(ls[w], ls[t] + 1) * invpes[ds[w]];
	v = e.v;
	l = ds[t] - ds[w] + 1;
      }
      else if (w == t) {
	mi e = st0.sum_range(ls[w], ls[s] + 1) * invpes[maxd - ds[s]];
	v = e.v;
	l = ds[s] - ds[w] + 1;
      }
      else {
	mi e0 = st0.sum_range(ls[w], ls[s] + 1) * invpes[maxd - ds[s]];
	int l0 = ds[s] - ds[w] + 1;
	mi e1 = st1.sum_range(ls[w] + 1, ls[t] + 1) * invpes[ds[w] + 1];
	int l1 = ds[t] - ds[w];
	mi e = e0 + e1 * pes[l0];
	v = e.v;
	l = l0 + l1;
      }
      //printf("v=%d, l=%d\n", v, l);

      if (hs[l].find(v) == hs[l].end()) {
	hs[l].insert(v);
	cnt++;
      }

      printf("%d\n", cnt);
    }
    else {
      int k;
      char cs[4];
      scanf("%d%s", &k, cs); k--;
      char c = cs[0];

      ks[k] = c;
      mi pk0 = pes[maxd - ds[k]] * ks[k];
      mi pk1 = pes[ds[k]] * ks[k];
      st0.set(ls[k], pk0);
      st0.set(rs[k], -pk0);
      st1.set(ls[k], pk1);
      st1.set(rs[k], -pk1);
    }
  }
  return 0;
}
accuracy: 0.8235294117647058
time_ms: 90
memory_kb: 30648
final_rank: 18

code_snippet: /* -*- coding: utf-8 -*-
 *
 * 0478.cc: Tree Keyboard
 */

#include<cstdio>
#include<vector>
#include<set>
#include<algorithm>
 
using namespace std;

/* constant */

const int MAX_N = 100000;
const int BN = 17;
const int P = 4073;
const int MOD = 1000000009;

/* typedef */

typedef vector<int> vi;
typedef set<int> si;

template<const int MOD>
struct MI {
  int v;
  MI(): v() {}
  MI(int _v): v(_v % MOD) {}
  MI(long long _v): v(_v % MOD) {}

  MI operator+(const MI m) const { return MI(v + m.v); }
  MI operator-(const MI m) const { return MI(v + MOD - m.v); }
  MI operator*(const MI m) const { return MI((long long)v * m.v); }
  MI operator-() const { return MI(MOD - v); }

  MI &operator+=(const MI m) { return (*this = *this + m); }
  MI &operator-=(const MI m) { return (*this = *this - m); }
  MI &operator*=(const MI m) { return (*this = *this * m); }

  bool operator==(const MI m) const { return v == m.v; }
  bool operator!=(const MI m) const { return v != m.v; }

  MI pow(int n) const {  // a^n % MOD
    MI pm = 1, a = *this;
    while (n > 0) {
      if (n & 1) pm *= a;
      a *= a;
      n >>= 1;
    }
    return pm;
  }

  MI inv() const { return pow(MOD - 2); }
  MI operator/(const MI m) const { return *this * m.inv(); }
  MI &operator/=(const MI m) { return (*this = *this / m); }
};

typedef MI<MOD> mi;

template <typename T>
struct SegTreeSum {
  int e2;
  vector<T> nodes;
  T defv;
  SegTreeSum() {}

  void init(int n, T _defv) {
    defv = _defv;
    for (e2 = 1; e2 < n; e2 <<= 1);
    nodes.assign(e2 * 2, defv);
  }

  T &geti(int i) { return nodes[e2 - 1 + i]; }
  void seti(int i, T v) { geti(i) = v; }

  void setall() {
    for (int j = e2 - 2; j >= 0; j--)
      nodes[j] = nodes[j * 2 + 1] + nodes[j * 2 + 2];
  }

  void set(int i, T v) {
    int j = e2 - 1 + i;
    nodes[j] = v;
    while (j > 0) {
      j = (j - 1) / 2;
      nodes[j] = nodes[j * 2 + 1] + nodes[j * 2 + 2];
    }
  }

  T sum_range(int r0, int r1, int k, int i0, int i1) {
    if (r1 <= i0 || i1 <= r0) return defv;
    if (r0 <= i0 && i1 <= r1) return nodes[k];

    int im = (i0 + i1) / 2;
    T v0 = sum_range(r0, r1, k * 2 + 1, i0, im);
    T v1 = sum_range(r0, r1, k * 2 + 2, im, i1);
    return v0 + v1;
  }
  T sum_range(int r0, int r1) { return sum_range(r0, r1, 0, 0, e2); }
};

/* global variables */

char ks[MAX_N];
vi nbrs[MAX_N];
int ps[MAX_N][BN], ds[MAX_N], cis[MAX_N], ls[MAX_N], rs[MAX_N];
mi pes[MAX_N + 1], invpes[MAX_N + 1];
SegTreeSum<mi> st0, st1;
si hs[MAX_N + 1];

/* subroutines */

inline void prep_rhash(int n) {
  pes[0] = invpes[0] = 1;
  pes[1] = P;
  invpes[1] = pes[1].inv();
  for (int i = 2; i <= n; i++) {
    pes[i] = pes[i - 1] * P;
    invpes[i] = invpes[i - 1] * invpes[1];
  }
}

inline int ancestor(int u, int d) {
  for (int i = BN - 1; i >= 0; i--)
    if ((d >> i) & 1) u = ps[u][i];
  return u;
}

int find_lca(int u, int v) {
  if (ds[u] > ds[v]) swap(u, v);

  v = ancestor(v, ds[v] - ds[u]);
  if (u == v) return u;

  for(int i = BN - 1; i >= 0; i--)
    if (ps[u][i] != ps[v][i])
      u = ps[u][i], v = ps[v][i];
  return ps[u][0];
}

/* main */

int main() {
  int n;
  scanf("%d", &n);

  for (int i = 0; i < n; i++) {
    char s[4];
    scanf("%s", s);
    ks[i] = s[0];
  }

  for (int i = 1; i < n; i++) {
    int u, v;
    scanf("%d%d", &u, &v);
    u--, v--;
    nbrs[u].push_back(v);
    nbrs[v].push_back(u);
  }

  int maxd = 0;
  ps[0][0] = -1, ds[0] = 0;
  for (int u = 0, l = 0; u >= 0;) {
    vi &nbru = nbrs[u];
    int up = ps[u][0];

    if (cis[u] == 0) ls[u] = l++;
    if (cis[u] < nbru.size()) {
      int v = nbru[cis[u]++];
      if (v != up) {
	ps[v][0] = u, ds[v] = ds[u] + 1;
	maxd = max(maxd, ds[v]);
	u = v;
      }
    }
    else {
      rs[u] = l++;
      u = up;
    }
  }
  //printf("maxd=%d\n", maxd);

  for (int i = 0; i < BN - 1; i++)
    for (int u = 0; u < n; u++)
      ps[u][i + 1] = (ps[u][i] >= 0) ? ps[ps[u][i]][i] : -1;

  prep_rhash(maxd);
  st0.init(n * 2, 0);
  st1.init(n * 2, 0);

  for (int u = 0; u < n; u++) {
    mi pk0 = pes[maxd - ds[u]] * ks[u];
    mi pk1 = pes[ds[u]] * ks[u];
    st0.seti(ls[u], pk0);
    st0.seti(rs[u], -pk0);
    st1.seti(ls[u], pk1);
    st1.seti(rs[u], -pk1);
  }
  st0.setall();
  st1.setall();

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

  for (int cnt = 0; qn--;) {
    int op;
    scanf("%d", &op);

    if (op == 1) {
      int s, t;
      scanf("%d%d", &s, &t);
      s--, t--;

      int w = find_lca(s, t);
      //printf("s=%d, t=%d, w=%d\n", s, t, w);

      int v, l;
      if (w == s) {
	mi e = st1.sum_range(ls[w], ls[t] + 1) * invpes[ds[w]];
	v = e.v;
	l = ds[t] - ds[w] + 1;
      }
      else if (w == t) {
	mi e = st0.sum_range(ls[w], ls[s] + 1) * invpes[maxd - ds[s]];
	v = e.v;
	l = ds[s] - ds[w] + 1;
      }
      else {
	mi e0 = st0.sum_range(ls[w], ls[s] + 1) * invpes[maxd - ds[s]];
	int l0 = ds[s] - ds[w] + 1;
	mi e1 = st1.sum_range(ls[w] + 1, ls[t] + 1) * invpes[ds[w] + 1];
	int l1 = ds[t] - ds[w];
	mi e = e0 + e1 * pes[l0];
	v = e.v;
	l = l0 + l1;
      }
      //printf("v=%d, l=%d\n", v, l);

      if (hs[l].find(v) == hs[l].end()) {
	hs[l].insert(v);
	cnt++;
      }

      printf("%d\n", cnt);
    }
    else {
      int k;
      char cs[4];
      scanf("%d%s", &k, cs); k--;
      char c = cs[0];

      ks[k] = c;
      mi pk0 = pes[maxd - ds[k]] * ks[k];
      mi pk1 = pes[ds[k]] * ks[k];
      st0.set(ls[k], pk0);
      st0.set(rs[k], -pk0);
      st1.set(ls[k], pk1);
      st1.set(rs[k], -pk1);
    }
  }
  return 0;
}
accuracy: 0.7647058823529411
time_ms: 90
memory_kb: 28376
final_rank: 19

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

using std::vector;
using std::array;
using std::pair;
using std::tuple;

using ll = long long;
using ull = unsigned long long;

constexpr ull MOD = ((ull)1 << 61) - 1;
const ull BASE = 0xABCDEF;

constexpr ull sum(ull a, const ull b) { return (a += b) >= MOD ? a - MOD : a; }
constexpr ull dif(ull a, const ull b) { return (a += MOD - b) >= MOD ? a - MOD : a; }

constexpr ull prod(const ull a, const ull b) {
    __uint128_t ret = (__uint128_t)a * b;
    ret = (ret >> 61) + (ret & MOD);
    return ret >= MOD ? ret - MOD : ret;
}

struct String {
    ull x, y, b;
    String() : x(0), y(0), b(1) {}
    explicit String(const char c) : x(c), y(c), b(BASE) {}
    explicit String(const ll x, const ll y, const ll b) : x(x), y(y), b(b) {}
    String operator+(const String& t) const {
        return String(sum(x, prod(t.x, b)), sum(t.y, prod(y, t.b)), prod(b, t.b));
    }
    String rev() const {
        return String(y, x, b);
    }
};

template <class F>
struct Fixed : private F {
    explicit Fixed(F&& f) : F(std::forward<F>(f)) {}
    template <class... Args> decltype(auto) operator()(Args&&... args) const {
        return F::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class F>
decltype(auto) fixed(F&& f) {
    return Fixed<F>(std::forward<F>(f));
}

struct Segment {
    int size;
    vector<String> data;
    explicit Segment(const int n) : size(n), data(2 * n) {}
    void set(int i, const char c) {
        i += size;
        data[i] = String(c);
        while (i > 1) {
            i >>= 1;
            data[i] = data[2 * i] + data[2 * i + 1];
        }
    }
    String fold(int l, int r) const {
        l += size;
        r += size;
        String L, R;
        while (l < r) {
            if (l & 1) L = L + data[l++];
            if (r & 1) R = data[--r] + R;
            l >>= 1;
            r >>= 1;
        }
        return L + R;
    }
};

struct HLD {
    struct Data {
        int par, sub, in, head;
    };
    vector<Data> node;
    explicit HLD(const vector<vector<int>>& graph) {
        const int n = (int)graph.size();
        node.resize(n);
        fixed([&](auto&& dfs, const int u, const int p) -> void {
            node[u].par = p;
            node[u].sub = 1;
            for (const int v : graph[u]) {
                if (v != p) {
                    dfs(v, u);
                    node[u].sub += node[v].sub;
                }
            }
        })(0, -1);
        int stamp = 0;
        fixed([&](auto&& dfs, const int u, const int h) -> void {
            node[u].head = h;
            node[u].in = stamp++;
            int s = -1;
            for (const int v : graph[u]) {
                if (v != node[u].par and (s == -1 or node[s].sub < node[v].sub)) {
                    s = v;
                }
            }
            if (s == -1) {
                return;
            }
            dfs(s, h);
            for (const int v : graph[u]) {
                if (v != node[u].par and v != s) {
                    dfs(v, v);
                }
            }
        })(0, 0);
    }
    int lca(int u, int v) const {
        if (node[u].in > node[v].in) {
            std::swap(u, v);
        }
        while (node[u].in < node[v].in) {
            if (node[u].head == node[v].head) {
                return u;
            }
            v = node[node[v].head].par;
        }
        return v;
    }
    template <class F>
    void path(int low, int high, const F& f) const {
        while (node[low].head != node[high].head) {
            const int h = node[low].head;
            f(h, low);
            low = node[h].par;
        }
        f(high, low);
    }
    int id(const int u) const {
        return node[u].in;
    }
};

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int N;
    std::cin >> N;
    vector<char> K(N);
    for (auto& c : K) {
        std::cin >> c;
    }
    vector<vector<int>> graph(N);
    for (int i = 0; i < N - 1; ++i) {
        int u, v;
        std::cin >> u >> v;
        u -= 1, v -= 1;
        graph[u].push_back(v);
        graph[v].push_back(u);
    }
    HLD hld(graph);
    Segment seg(N);
    for (int i = 0; i < N; ++i) {
        seg.set(hld.id(i), K[i]);
    }
    int Q;
    std::cin >> Q;
    std::set<ull> set;
    while (Q--) {
        int t;
        std::cin >> t;
        if (t == 1) {
            int a, b;
            std::cin >> a >> b;
            a -= 1;
            b -= 1;
            const int c = hld.lca(a, b);
            String L, R;
            hld.path(a, c, [&](const int x, const int y) {
                L = seg.fold(hld.id(x), hld.id(y) + 1) + L;
            });
            hld.path(b, c, [&](const int x, const int y) {
                if (x == c) {
                    R = seg.fold(hld.id(x) + 1, hld.id(y) + 1) + R;
                } else {
                    R = seg.fold(hld.id(x), hld.id(y) + 1) + R;
                }
            });
            set.insert((L.rev() + R).x);
            std::cout << set.size() << '\n';
        } else {
            int k;
            char c;
            std::cin >> k >> c;
            k -= 1;
            seg.set(hld.id(k), c);
        }
    }
    return 0;
}
accuracy: 1
time_ms: 110
memory_kb: 27288
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<(int)(n);i++)
using ll=long long;
const char newl='\n';

constexpr int m[]={1000000007,998244353};
//原始根：m1…5,m2…3
const ll base=12346;
ll base_pow[2][100001];

struct S{
    ll v1,v2;
    int len;
    S():v1(0),v2(0),len(0){}
    S(char c):v1(c-'a'+1),v2(c-'a'+1),len(1){}
};
//aの右にbを結合する
S mul(S a,S b){
    a.len+=b.len;
    a.v1=(a.v1*base_pow[0][b.len] + b.v1)%m[0];
    a.v2=(a.v2*base_pow[1][b.len] + b.v2)%m[1];
    return a;
};
struct segtree{
    int sz;
    vector<S> val[2];
    segtree(int n):sz(1){
        while(sz<n)sz*=2;
        val[0].resize(2*sz),val[1].resize(2*sz);
    }
    void change(int i,char c){
        S x(c);
        i+=sz;
        val[0][i]=val[1][i]=x;
        i/=2;
        while(i){
            val[0][i]=mul(val[0][2*i],val[0][2*i+1]);
            val[1][i]=mul(val[1][2*i+1],val[1][2*i]);
            i/=2;
        }
    }
    S prod(int l,int r,int d){
        l+=sz;
        r+=sz;
        S ans1,ans2;
        while(l<r){
            if(l&1){
                ans1=(d==0 ? mul(ans1,val[0][l]) : mul(val[1][l],ans1));
                l++;
            }
            if(r&1){
                --r;
                ans2=(d==0 ? mul(val[0][r],ans2) : mul(ans2,val[1][r]));
            }
            l/=2,r/=2;
        }
        return (d==0 ? mul(ans1,ans2) : mul(ans2,ans1));
    }
};

// やること
// 分解パート
// 1.部分木サイズ計算　この時、heavyな辺が子供一覧の先頭に来るようにする
// 2.頂点に番号を振っていく　この時、nxtなる配列に自分の属する列の一番高い場所を記録する
// 処理パート
// 1.lcaの準備
// 2.クエリ処理（1点クエリはやるだけ、そうでなければlcaまでのパスで分割　非可換なので、頑張る）
// 実装上の注意：根からの同一パス上では、inが大きいこととより深い頂点であることが同値
struct hld{
    int n;
    vector<int> in,next,sz,p;
    vector<vector<int>> c;
    segtree seg;
    hld(const int n,vector<vector<int>> &g):in(n),next(n),sz(n),p(n,-1),c(n),seg(n){
        //p,cをgに沿って更新
        queue<int> q;
        q.emplace(0);
        while(!q.empty()){
            int now=q.front();q.pop();
            for(int w:g[now])if(w!=p[now]){
                p[w]=now;
                c[now].emplace_back(w);
                q.emplace(w);
            }
        }
        int t=0;
        dfs1();
        dfs2(t);
    }

    void dfs1(int v=0){
        sz[v]=1;
        for(int& w:c[v]){
            dfs1(w);
            sz[v]+=sz[w];
            if(sz[w] > sz[c[v][0]])swap(w,c[v][0]);
        }
    }
    void dfs2(int& t,int v=0){
        in[v]=t++;
        for(int w:c[v]){
            next[w]=(w==c[v][0] ? next[v]:w);
            dfs2(t,w);
        }
    }
    int lca(int u,int v){
        while(true){
            if(in[u]>in[v])swap(u,v);
            if(next[u]==next[v])return u;
            v=p[next[v]];
        }
    }
    void update(int i,char c){
        seg.change(in[i],c);
    }
    S query(int u,int v){
        int l=lca(u,v);
        S ans1,ans2;
        while(next[u]!=next[l]){
            ans1=mul(ans1,seg.prod(in[next[u]],in[u]+1,1));
            u=p[next[u]];
        }
        ans1=mul(ans1,seg.prod(in[l],in[u]+1,1));
        while(next[v]!=next[l]){
            ans2=mul(seg.prod(in[next[v]],in[v]+1,0),ans2);
            v=p[next[v]];
        }
        //片方ではin[l]を含むとダメ！
        ans2=mul(seg.prod(in[l]+1,in[v]+1,0),ans2);
        return mul(ans1,ans2);
    }
};

ll powmod(ll a,ll b,const int mod){
    if(b==1)return a;
    ll v=powmod(a,b/2,mod);
    v*=v;
    v%=mod;
    if(b&1){
        v*=a;
        v%=mod;
    }
    return v;
}

int main(){
    rep(i,2)base_pow[i][0]=1;
    rep(i,2)rep(j,100000)base_pow[i][j+1]=base_pow[i][j]*base%m[i];

    int n;
    cin >> n;
    vector<char> beg(n);
    rep(i,n)cin >> beg[i];
    vector<vector<int>> g(n);
    rep(i,n-1){
        int u,v;
        cin >> u >> v;
        u--,v--;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }
    hld h(n,g);
    rep(i,n)h.update(i,beg[i]);

    int q;
    cin >> q;
    set<pair<ll,ll>> st;
    while(q--){
        int com;
        cin >> com;
        if(com==1){
            int s,t;
            cin >> s >> t;
            s--,t--;
            S res=h.query(s,t);
            st.insert({res.v1,res.v2});
            cout << st.size() << newl;
        }
        else{
            int k;
            char c;
            cin >> k >> c;
            k--;
            h.update(k,c);
        }
    }
}
accuracy: 1
time_ms: 200
memory_kb: 41992
final_rank: 12