#include using namespace std; using ll=long long; using ld=double; #define FOR(i,a,b) for(ll i=a;i<(ll)b;++i) #define F(n) FOR(i,0,n) #define FF(n) FOR(j,0,n) #define aa first #define bb second #define PB push_back #define EQ(a,b) (fabs(a-b)<=(fabs(a+b)*EPS)) #define MOD ((ll)(1e9+7)) struct UnionFindOp { int v; int hisParent; int changedDepth; bool u; UnionFindOp(int v, int h, int changedDepth, bool u): v(v), hisParent(h), changedDepth(changedDepth), u(u) {} UnionFindOp() = default; }; UnionFindOp history[(ll)(1e7)]; int hp = 0; ll n, m, bl; int st, en; struct UnionFind { vector parent; vector depth; UnionFind() = default; UnionFind(int n): parent(vector(n)), depth(vector(n, 0)) { for (int i = 0; i < n; ++ i) parent[i] = i; hp = 0; } // nalezne koren pro vrchol v // O(log(n)) // u perzistentni verze je na datech ~2x rychlejsi nez log* int find(int v) { assert(v < n); if (v == parent[v]) { return v; } else { return find(parent[v]); } } // zjisti, jestli jsou ve stejne komponente bool check(int a, int b) { return find(a) == find(b); } // spoji dva vrcholy do jedne komponenty void do_union(int a, int b) { if (check(a, b)) { history[hp] = UnionFindOp(a, parent[a], -1, true); // nic neudela hp ++; return; } int ar = find(a), br = find(b); assert(hp >= 0 && hp < 1e6); if (depth[ar] < depth[br]) { history[hp] = (UnionFindOp(ar, parent[ar], -1, true)); hp ++; parent[ar] = br; } else if (depth[ar] > depth[br]) { history[hp] = (UnionFindOp(br, parent[br], -1, true)); hp ++; parent[br] = ar; } else { history[hp] = (UnionFindOp(br, parent[br], ar, true)); hp ++; parent[br] = ar; depth[ar] ++; } } // move one step back in history void goBack() { const auto & t = history[hp - 1]; parent[t.v] = t.hisParent; if (t.changedDepth != -1) depth[t.changedDepth] --; hp --; if (hp > 0 && !history[hp - 1].u) goBack(); } void clear() { hp = 0; iota(parent.begin(), parent.end(), 0); fill(depth.begin(), depth.end(), 0); } }; vector> e; vector> qr; UnionFind uf; bool cyc = false; int shakySize = 0; int shakyStart; // move end of the interval void addEdge() { en ++; if (en >= m) return; if (shakySize >= bl) { // if the shaky part is too big, rebuild the whole union find uf.clear(); for (int i = en - 1; i >= st; -- i) { uf.do_union(e[i].aa, e[i].bb); } // we need to keep the last edge in the shaky part shakySize = 1; cyc |= uf.check(e[en].aa, e[en].bb); uf.do_union(e[en].aa, e[en].bb); shakyStart = en; } else { cyc |= uf.check(e[en].aa, e[en].bb); uf.do_union(e[en].aa, e[en].bb); shakySize ++; } } // move start of the interval void removeEdge() { if (shakySize == en - st + 1) { F(shakySize) uf.goBack(); shakySize --; shakyStart ++; } else { // after removing the shaky part, + 1 makes sure we remove the leftmost edge F(shakySize + 1) uf.goBack(); } // the last added edge must have caused the cycle, therefore there is none now cyc = false; // put the shaky part back now that we have removed the leftmost edge F(shakySize) { const int u = e[i + shakyStart].aa; const int v = e[i + shakyStart].bb; cyc |= uf.check(u, v); uf.do_union(u, v); } st ++; } int main(){ ios::sync_with_stdio(0);cin.tie(0); cin >> n >> m; e = vector>(m); uf = UnionFind(n); bl = max(4ll, (ll)sqrt(m)); // maximum size of the shaky part F(m) { cin >> e[i].aa >> e[i].bb; e[i].aa --; e[i].bb --; } ll q; cin >> q; qr = vector>(q); st = 0; en = -1; cyc = false; vector ends(m); // for each start, this saves the longest end such that [start, ends[starts]] has no cycle vector sizes(m); vector pf(m); while (en < m) { // add edge by increasing en addEdge(); if (en == m) break; // move st while there is a cycle while (cyc) { removeEdge(); } // here it holds that [st, en] has no cycle ends[st] = en; sizes[st] = en - st + 1; } for (int i = 1; i < m; ++ i) if (ends[i] == 0) ends[i] = ends[i - 1]; for (int i = 0; i < m; ++ i) sizes[i] = ends[i] - i + 1; // given a set of starts for which [start, ends[start]] completely fits into the query // this allows us to compute the answer for the query pf[0] = sizes[0]; F(m-1) pf[i+1] = pf[i] + sizes[i+1]; // now compute for each query the result F(q) { ll a, b; cin >> a >> b; a --; b --; // find the first start such that ends[start] is beyong the query auto it = lower_bound(ends.begin(), ends.end(), b); int idx = it - ends.begin(); ll ans = 0; if (idx <= a) { // the whole query is without a cycle ans = (b - a + 2) * (b - a + 1) / 2; } else { // idx > a // for all starts expect the last, we sum the result ans += pf[idx - 1]; if (a > 0) ans -= pf[a - 1]; // for the last start, count the number of subintervals of [start, b] ans += max(0ll, (b - idx + 2) * (b - idx + 1) / 2); } cout << ans << '\n'; } return 0; }