n n n个点 m m m条边的无向图,询问保留图中编号在 [ l , r ] [l,r] [l,r]的边的时候图中的联通块个数。
n , m ≤ 200000 n,m \leq 200000 n,m≤200000
考虑常见套路:联通块数等于点数 − - −树边数。
从小到大加边,维护最大生成树。求出 p r e i pre_i prei表示 i i i这条边需要删除哪条边, p r e i = 0 pre_i=0 prei=0表示没有删除任何边。
对于一组询问 [ l , r ] [l,r] [l,r],如果 p r e i < l ≤ i ≤ r pre_i
#include
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 400005;
int n, m, q, tp, eu[maxn], ev[maxn], val[maxn];
int f[maxn], ch[maxn][2], rev[maxn], Min[maxn];
#define get(x) (ch[f[x]][1] == x)
#define is_root(x) (ch[f[x]][0] != x && ch[f[x]][1] != x)
#define update(x) (Min[x] = min(val[x], min(Min[ch[x][0]], Min[ch[x][1]])))
inline void rotate(int x)
{
int fa = f[x], gfa = f[fa], k = get(x);
ch[fa][k] = ch[x][k ^ 1]; f[ch[x][k ^ 1]] = fa;
ch[x][k ^ 1] = fa;
if (!is_root(fa)) ch[gfa][get(fa)] = x;
f[fa] = x; f[x] = gfa;
update(fa);
}
#define push_rev(x) (swap(ch[x][0], ch[x][1]), rev[x] ^= 1)
inline void pushdown(int x)
{
if (!rev[x]) return ;
if (ch[x][0]) push_rev(ch[x][0]);
if (ch[x][1]) push_rev(ch[x][1]);
rev[x] = 0;
}
inline void splay(int x)
{
static int stk[maxn], top, fa;
stk[top = 1] = x;
while (!is_root(x)) stk[++top] = x = f[x];
while (top) pushdown(stk[top--]);
x = stk[1];
while (!is_root(x)) {
fa = f[x];
if (!is_root(fa))
get(x) ^ get(fa) ? rotate(x) : rotate(fa);
rotate(x);
}
update(x);
}
inline void access(int x)
{
for (int y = 0; x; y = x, x = f[x])
splay(x), ch[x][1] = y, update(x);
}
inline void make_root(int x) {access(x); splay(x); push_rev(x);}
inline void link(int x, int y) {make_root(x); make_root(y); f[y] = x;}
inline void cut(int x, int y) {make_root(x); access(y); splay(y); ch[y][0] = f[x] = 0;}
inline int query(int x, int y) {make_root(x); access(y); splay(y); return Min[y];}
inline int find(int x) {access(x); splay(x); while (ch[x][0]) x = ch[x][0]; splay(x); return x;}
int pre[maxn];
void kruscal()
{
Min[0] = 1e9;
for (int i = 1; i <= n; ++i) Min[i] = val[i] = 1e9;
for (int i = 1; i <= m; ++i) Min[i + n] = val[i + n] = i;
for (int i = 1; i <= m; ++i) {
if (eu[i] == ev[i]) continue;
if (find(eu[i]) == find(ev[i])) {
pre[i] = query(eu[i], ev[i]);
cut(pre[i] + n, eu[pre[i]]);
cut(pre[i] + n, ev[pre[i]]);
}
link(i + n, eu[i]);
link(i + n, ev[i]);
}
}
int rt[maxn], lch[maxn * 60], rch[maxn * 60], sum[maxn * 60], tot;
#define mid ((l + r) >> 1)
void insert(int &s, int l, int r, int p)
{
++tot;
lch[tot] = lch[s]; rch[tot] = rch[s]; sum[tot] = sum[s] + 1;
s = tot;
if (l == r) return ;
if (p <= mid) insert(lch[s], l, mid, p);
else insert(rch[s], mid + 1, r, p);
}
int query(int s1, int s2, int l, int r, int p)
{
if (r <= p) return sum[s2] - sum[s1];
if (p <= mid) return query(lch[s1], lch[s2], l, mid, p);
else return query(lch[s1], lch[s2], l, mid, p) + query(rch[s1], rch[s2], mid + 1, r, p);
}
int main()
{
//freopen("gerald.in", "r", stdin);
freopen("8.in", "r", stdin);
freopen("gerald.out", "w", stdout);
n = gi(); m = gi(); q = gi(); tp = gi();
for (int i = 1; i <= m; ++i) eu[i] = gi(), ev[i] = gi();
kruscal();
for (int i = 1; i <= m; ++i) {
rt[i] = rt[i - 1];
if (eu[i] != ev[i]) insert(rt[i] = rt[i - 1], 0, m, pre[i]);
}
int lstans = 0;
for (int l, r, i = 1; i <= q; ++i) {
l = gi() ^ lstans; r = gi() ^ lstans;
printf("%d\n", lstans = (n - query(rt[l - 1], rt[r], 0, m, l - 1)));
if (!tp) lstans = 0;
}
return 0;
}