1. 根据题目给出的关系,可以将所有的人利用并查集分组。
  2. 分组后,可以使用莫队算法,窗口维护一个数组,代表了种类出现的次数,还需要维护,代表了个数。维护方式很简单,不多做赘述,见代码即可。
#include <bits/stdc++.h>
#define close ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
#define int long long
using namespace std;
const int N = 1e5 + 5;
const int M = 1e5 + 7;


int n,m,q;
int fa[N];
int find(int x){
    if(fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}
int a[N],res[M];
int blen,bnum,bid[N];
struct Query{
    int l,r,id;
    bool operator < (const Query &oth){
        return bid[l] == bid[oth.l] ? r < oth.r : bid[l] < bid[oth.l];
    }
}queries[M];
//莫队维护信息
int kind = 0;
int cnt[N];
void init(){
    memset(cnt,0,sizeof(cnt));
    kind = 0;
    blen = sqrt(n);
    bnum = (n + blen - 1) / blen;
    for(int i = 1;i <= n;i++){
        bid[i] = (i - 1) / blen + 1;
    }
    sort(queries + 1,queries + q + 1);
}

void add(int x){
    if(cnt[x] == 0) kind++;
    cnt[x]++;
}
void del(int x){
    cnt[x]--;
    if(cnt[x] == 0) kind--;
}
void mo(){
    int wl = 1,wr = 0;
    for(int i = 1;i <= q;i++){
        auto [l,r,id] = queries[i];
        // l wl wr r
        while(wl > l) add(a[--wl]);
        while(wr < r) add(a[++wr]);
        while(wl < l) del(a[wl++]);
        while(wr > r) del(a[wr--]);
        res[id] = kind;
    }
    
}
void solve(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++) fa[i] = i;
    for(int i = 1;i <= m;i++){
        int u,v; cin >> u >> v;
        fa[find(u)] = find(v);
    }
    for(int i = 1;i <= n;i++){
        a[i] = find(i);
    }
    cin >> q;
    for(int i = 1;i <= q;i++){
        int l,r; cin >> l >> r;
        queries[i] = {l,r,i};
    }
    init();
    mo();
    for(int i = 1;i <= q;i++){
        cout << res[i] << endl;
    }
}
signed main() {
    close;
    int T = 1;cin >> T;
    while(T--){
        solve();
    }
    
    return 0;
}