#line 1 "main.cpp" #include #define REP(i, n) for(int i = 0; i < (int)(n); ++i) #define RREP(i, n) for(int i = (int)(n); i-- > 0;) #define FOR(i, l, r) for(int i = (int)(l); i < (int)(r); ++i) #define RFOR(i, l, r) for(int i = (int)(r); i-- > (int)(l);) #define ALL(v) std::begin(v), std::end(v) using llong = long long; using vi = std::vector; using vvi = std::vector; using pii = std::pair; using namespace std; constexpr int INF = 1e9; constexpr long long LINF = 1e18; constexpr double EPS = 1e-10; constexpr int MOD = 998'244'353; constexpr int MOD2 = 1e9 + 7; template > requires std::ranges::output_range inline auto &operator>>(std::basic_istream &is, R &r) { for(auto &elem : r) is >> elem; return is; } template requires(!std::convertible_to) inline auto &operator<<(std::basic_ostream &os, const R &r) { if(std::ranges::empty(r)) return os; auto iter = std::ranges::cbegin(r); const auto end = std::ranges::cend(r); os << *iter++; while(iter != end) os << " " << *iter++; return os; } #ifdef DEBUG #include #else #define debug(...) static_cast(0) #endif #line 1 "algorithm/String/trie.hpp" #line 10 "algorithm/String/trie.hpp" #include #line 12 "algorithm/String/trie.hpp" #include #line 14 "algorithm/String/trie.hpp" namespace algorithm { template class TrieBase { static_assert(std::is_invocable_r::value); public: using value_type = T; using size_type = std::size_t; using compare = Compare; protected: struct Node; using node_pointer = std::unique_ptr; struct Node { size_type total; // total:=(自身を根とする部分木に含まれる要素の数). size_type cnt; // cnt:=(自身を末尾とする要素の数). std::map next; // next[]:=(子のポインタ). Node() : total(0), cnt(0) {} }; node_pointer m_root; // m_root:=(根のポインタ). template Node *find(const node_pointer &p, Iter iter, Iter end) const { if(!p) return nullptr; if(iter == end) return p.get(); return find(p->next[*iter], std::next(iter), end); } template void add(node_pointer &p, Iter iter, Iter end, size_type n) { // top down. if(!p) p = std::make_unique(); p->total += n; if(iter == end) { p->cnt += n; return; } add(p->next[*iter], std::next(iter), end, n); } template size_type sub(node_pointer &p, Iter iter, Iter end, size_type n) { // top down. assert(p and p->total >= n); p->total -= n; if(p->total == 0) { p.reset(); return n; } if(iter == end) { p->cnt -= n; return n; } return sub(p->next[*iter], std::next(iter), end, n); } template size_type erase(node_pointer &p, Iter iter, Iter end) { // bottom up. if(!p) return 0; if(iter == end) { size_type res = p->cnt; p->total -= res, p->cnt = 0; if(p->total == 0) p.reset(); return res; } size_type res = erase(p->next[*iter], std::next(iter), end); p->total -= res; if(p->total == 0) p.reset(); return res; } template size_type erase_by_prefix(node_pointer &p, Iter iter, Iter end) { // bottom up. if(!p) return 0; if(iter == end) { size_type res = p->total; p.reset(); return res; } size_type res = erase_by_prefix(p->next[*iter], std::next(iter), end); p->total -= res; if(p->total == 0) p.reset(); return res; } template Sequence get(const node_pointer &p, size_type k, size_type depth = 0) const { // bottom up. if(k < p->cnt) return Sequence(depth, value_type()); k -= p->cnt; for(const auto &[key, next] : p->next) { if(!next) continue; if(k < next->total) { auto &&res = get(next, k, depth + 1); res[depth] = key; return res; } k -= next->total; } assert(false); // not reach here. } template std::pair lower_and_upper_bound(node_pointer &p, Iter iter, Iter end) const { // bottom up. if(!p) return {0, 0}; if(iter == end) return {0, p->cnt}; size_type offset = 0; for(const auto &[key, next] : p->next) { if(key > *iter) break; if(key == *iter) { auto &&[l, r] = lower_and_upper_bound(next, std::next(iter), end); return {l + offset, r + offset}; } offset += next->total; } return {offset, offset}; } template std::vector> get_nodes(const node_pointer &p, Iter iter, Iter end, size_type depth = 0) const { // bottom up. if(!p) { while(iter++ != end) ++depth; return std::vector>(depth + 1, {0, 0}); // return pairs of (total, cnt). } auto &&res = (iter == end ? std::vector>(depth + 1, {0, 0}) : get_nodes(p->next[*iter], std::next(iter), end, depth + 1)); res[depth] = {p->total, p->cnt}; return res; } public: TrieBase() : m_root(nullptr) {} ~TrieBase() { clear(); } bool empty() const { return size() == 0; } size_type size() const { return (m_root ? m_root->total : 0); } template bool exists(const R &r) const { return count(r) > 0; } template size_type count(const R &r) const { auto p = find(m_root, std::ranges::cbegin(r), std::ranges::cend(r)); return (p ? p->cnt : 0); } template bool exists_by_prefix(const R &r) const { return count_by_prefix(r) > 0; } template size_type count_by_prefix(const R &r) const { auto p = find(m_root, std::ranges::cbegin(r), std::ranges::cend(r)); return (p ? p->total : 0); } template void insert(const R &r, size_type n = 1) { if(n == 0) return; add(m_root, std::ranges::cbegin(r), std::ranges::cend(r), n); } template size_type erase(const R &r) { return erase(m_root, std::ranges::cbegin(r), std::ranges::cend(r)); } template size_type erase(const R &r, size_type n) { if(n == 0) return 0; return sub(m_root, std::ranges::cbegin(r), std::ranges::cend(r), n); } template size_type erase_by_prefix(const R &r) { return erase_by_prefix(m_root, std::ranges::cbegin(r), std::ranges::cend(r)); } template Sequence kth_element(size_type k) const { assert(k < size()); return get(m_root, k); } template Sequence min_element() const { return kth_element(0); } template Sequence max_element() const { return kth_element(size() - 1); } template std::pair lower_and_upper_bound(const R &r) const { return lower_and_upper_bound(m_root, std::ranges::cbegin(r), std::ranges::cend(r)); } template std::vector> get_nodes(const R &r) const { return get_nodes(m_root, std::ranges::cbegin(r), std::ranges::cend(r)); } void clear() { m_root.reset(); } }; template > class Trie : public TrieBase { public: using base_type = TrieBase; using typename base_type::compare; using typename base_type::size_type; using typename base_type::value_type; std::vector kth_element(size_type k) const { return base_type::template kth_element>(k); } std::vector min_element() const { return base_type::template min_element>(); } std::vector max_element() const { return base_type::template max_element>(); } }; template class Trie : public TrieBase { public: using base_type = TrieBase; using typename base_type::compare; using typename base_type::size_type; using typename base_type::value_type; // 多重集合内に文字列svが含まれるか判定する. bool exists(std::string_view sv) const { return base_type::exists(sv); } // 多重集合内に含まれる文字列svの個数を取得する. size_type count(std::string_view sv) const { return base_type::count(sv); } // 多重集合内に接頭辞prefixをもつ文字列が含まれるか判定する. bool exists_by_prefix(std::string_view prefix) const { return base_type::exists_by_prefix(prefix); } // 多重集合内に含まれる接頭辞prefixをもつ文字列の個数を取得する. size_type count_by_prefix(std::string_view prefix) const { return base_type::count_by_prefix(prefix); } // 多重集合に文字列svをn個追加する. void insert(std::string_view sv, size_type n = 1) { base_type::insert(sv, n); } // 多重集合から文字列svをすべて削除する. void erase(std::string_view sv) { base_type::erase(sv); } // 多重集合から文字列svをn個削除する. void erase(std::string_view sv, size_type n) { base_type::erase(sv, n); } // 多重集合から接頭辞prefixをもつ文字列をすべて削除する. void erase_by_prefix(std::string_view prefix) { base_type::erase_by_prefix(prefix); } // 多重集合内において,辞書順でk番目に小さい文字列を取得する. std::string kth_element(size_type k) const { return base_type::template kth_element(k); } // 多重集合内において,辞書順で最も小さい文字列を取得する. std::string min_element() const { return base_type::template min_element(); } // 多重集合内において,辞書順で最も大きい文字列を取得する. std::string max_element() const { return base_type::template max_element(); } std::pair lower_and_upper_bound(std::string_view sv) const { return base_type::lower_and_upper_bound(sv); } std::vector> get_nodes(std::string_view sv) const { return base_type::get_nodes(sv); } }; } // namespace algorithm #line 47 "main.cpp" int main() { int n; cin >> n; vector vs(n); cin >> vs; algorithm::Trie trie; for(const auto &s : vs) trie.insert(s); int q; cin >> q; while(q--) { int t; int x; cin >> t >> x; x--; if(t == 1) { char c; cin >> c; trie.erase(vs[x], 1); vs[x] += c; trie.insert(vs[x], 1); } else { int ans = 0; int len = 0; const auto &&nodes = trie.get_nodes(vs[x]); debug(vs[x]); debug(nodes); for(const auto &[total, cnt] : nodes) ans += len++ * cnt; ans += (len - 1) * (nodes.back().first - nodes.back().second); cout << ans << "\n"; } } }