- 根据题目给出的关系,可以将所有的人利用并查集分组。
- 分组后,可以使用莫队算法,窗口维护一个
数组,
代表了种类
出现的次数,还需要维护
,代表了个数。维护方式很简单,不多做赘述,见代码即可。
#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;
}