#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#define inline inline __attribute__((always_inline))

using namespace std;

template <class Value, int MAX_SIZE>
class BufferedContainer {
    Value buf[MAX_SIZE];
    int sz = 0;

public:
    inline int size() const { return sz; }
    inline void clear() { sz = 0; }
    inline bool empty() const { return sz == 0; }

    inline void push(Value value) { buf[sz++] = value; }
    inline Value pop() { return buf[--sz]; }

    inline Value operator[](int i) { return buf[i]; }

    inline Value *begin() { return buf; }
    inline Value *end() { return buf + sz; }
};

constexpr int V = 5e4;
constexpr int E = 1e5;
constexpr int Q = 1e5;
constexpr int B = 1000;

struct Edge {
    int u, v, w;
};

struct Query {
    int v, w, t;
};

struct Update {
    int e, w, t;
};

int temp[E];
Edge edges[E];
int edge_order[E];
Query queries[B];
Update updates[B];
BufferedContainer<int, B> additional[B];
bool changed[E];
BufferedContainer<int, B> changed_edges;
BufferedContainer<int, V> searching;
int visited[V];
int answer[B];

namespace DSU
{
    int parent[V];

    void init(int n) {
        fill(parent, parent + n, -1);
    }

    int find_set(int u) {
        if (parent[u] < 0) return u;
        return parent[u] = find_set(parent[u]);
    }

    void union_sets(int u, int v) {
        u = find_set(u);
        v = find_set(v);
        if (u == v) return;

        if (parent[u] > parent[v]) swap(u, v);
        parent[u] += parent[v];
        parent[v] = u;
    }
}

vector<pair<int, int>> graph[V];

namespace subtaskLinear {
    int n;
    int tree[4 * V];

    void build(int id, int l, int r) {
        if (l == r) {
            tree[id] = edges[l - 1].w;
            return;
        }
        int m = l + r >> 1;
        build(id * 2, l, m);
        build(id * 2 + 1, m + 1, r);
        tree[id] = min(tree[id * 2], tree[id * 2 + 1]);
    }

    void update(int id, int l, int r, int i, int w) {
        if (l == r) {
            tree[id] = w;
            return;
        }
        int m = l + r >> 1;
        if (i <= m)
            update(id * 2, l, m, i, w);
        else
            update(id * 2 + 1, m + 1, r, i, w);
        tree[id] = min(tree[id * 2], tree[id * 2 + 1]);
    }

    int walk_left(int id, int l, int r, int i, int w) {
        if (l >= i) return 0;
        if (r < i) {
            if (tree[id] >= w) return 0;
            if (l == r) return l;
        }
        int m = l + r >> 1;
        int res = walk_left(id * 2 + 1, m + 1, r, i, w);
        return res ? res : walk_left(id * 2, l, m, i, w);
    }

    int walk_right(int id, int l, int r, int i, int w) {
        if (r < i) return n;
        if (l >= i) {
            if (tree[id] >= w) return n;
            if (l == r) return l;
        }
        int m = l + r >> 1;
        int res = walk_right(id * 2, l, m, i, w);
        return res != n ? res : walk_right(id * 2 + 1, m + 1, r, i, w);
    }

    void solve(int n) {
        if (n == 1) {
            int q;
            cin >> q;

            while (q--) {
                cout << '1' << '\n';
            }
            return;
        }

        subtaskLinear::n = n;
        build(1, 1, n - 1);

        int q;
        cin >> q;

        while (q--) {
            char type;
            cin >> type;

            int i, w;
            cin >> i >> w;

            if (type == '1') {
                update(1, 1, n - 1, i, w);
                continue;
            }

            cout << walk_right(1, 1, n - 1, i, w) - walk_left(1, 1, n - 1, i, w) << '\n';
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    // freopen("input.txt", "r", stdin);

    int nV, nE;
    cin >> nV >> nE;

    bool is_linear = nE == nV - 1;

    for (int e = 0; e < nE; e++) {
        int u, v, w;
        cin >> u >> v >> w;
        u--, v--;

        is_linear &= u == e && v == e + 1;
        edges[e] = {u, v, w};
    }

    if (is_linear) {
        subtaskLinear::solve(nV);
        return 0;
    }

    iota(edge_order, edge_order + nE, 0);
    sort(edge_order, edge_order + nE, [](int i, int j) {
        return edges[i].w > edges[j].w;
    });

    int nQU;
    cin >> nQU;
    int timer = 0;

    for (int block_start = 0; block_start < nQU; block_start += B) {
        const int block_size = min(B, nQU - block_start);

        int nQ = 0, nU = 0;
        changed_edges.clear();

        for (int t = 0; t < block_size; t++) {
            char type;
            cin >> type;

            int i, w;
            cin >> i >> w;
            i--;

            if (type == '1') {
                updates[nU++] = {i, w, nQ};
                if (!changed[i]) {
                    changed[i] = true;
                    changed_edges.push(i);
                }
            } else {
                queries[nQ] = {i, w, nQ};
                nQ++;
            }
        }

        int u = 0;
        for (int q = 0; q < nQ; q++) {
            while (u < nU && updates[u].t <= queries[q].t) {
                edges[updates[u].e].w = updates[u].w;
                u++;
            }
            additional[q].clear();
            for (int e : changed_edges) {
                if (edges[e].w >= queries[q].w) {
                    additional[q].push(e);
                }
            }
        }
        while (u < nU) {
            edges[updates[u].e].w = updates[u].w;
            u++;
        }

        sort(queries, queries + nQ, [](const Query &p, const Query &q) {
            return p.w > q.w;
        });

        DSU::init(nV);
        int i = 0;

        for (int q = 0; q < nQ; q++) {
            int w = queries[q].w;

            while (i < nE) {
                int e = edge_order[i];
                if (changed[e]) { i++; continue; }
                if (edges[e].w < w) break;
                DSU::union_sets(edges[e].u, edges[e].v);
                i++;
            }

            timer++;

            for (int e : additional[queries[q].t]) {
                int u = DSU::find_set(edges[e].u);
                int v = DSU::find_set(edges[e].v);
                if (u == v) continue;
                graph[u].emplace_back(v, timer);
                graph[v].emplace_back(u, timer);
            }

            searching.clear();
            int v = DSU::find_set(queries[q].v);
            searching.push(v);
            visited[v] = timer;
            int res = 0;

            while (!searching.empty()) {
                int u = searching.pop();
                res -= DSU::parent[u];

                for (auto [v, t] : graph[u]) {
                    if (t != timer) continue;
                    if (visited[v] == timer) continue;
                    visited[v] = timer;
                    searching.push(v);
                }
                graph[u].clear();
            }

            answer[queries[q].t] = res;
        }

        for (int q = 0; q < nQ; q++) {
            cout << answer[q] << '\n';
        }

        sort(changed_edges.begin(), changed_edges.end(), [](int e, int f) {
            return edges[e].w > edges[f].w;
        });

        i = 0;
        while (i < nE && changed[edge_order[i]]) i++;

        for (int j = 0, k = 0; k < nE; k++) {
            if (j == changed_edges.size() || i < nE && edges[edge_order[i]].w >= edges[changed_edges[j]].w) {
                temp[k] = edge_order[i++];
                while (i < nE && changed[edge_order[i]]) i++;
            } else {
                temp[k] = changed_edges[j++];
            }
        }
        copy(temp, temp + nE, edge_order);

        for (int e : changed_edges) {
            changed[e] = false;
        }
    }

    return 0;
}