题意简单明了。。。
考虑从头开始加边。如果还是一棵树的时候,加入了一条边会怎么样。
如果还是树就不鸟他。
但是如果构成了环,那么这个环要存在的必要条件显然是最早的边要存在。(当然这个环上的其他边也要存在,但这不重要,因为如果最早的边存在了,这些边肯定会存在),如果将当前边设为 Ej ,这个环上标号最小的边设为 Ei ,那我们称 Ej 依赖于 Ei 。因为如果当前是棵树的情况下,如果 Ei 不在里面,即使 Ej 加入了也对树结构没有影响。因我们可以得到一个算法,按顺序加边,如果加入边 (u,v) 后出现了环,那么就查询 u→v 的路径上标号最小的边,并把它删掉,将当前边的权值设为它,然后再加入当前边。区间 [l,r] 内的答案就是n-“区间内权值小于l的数的个数”。
于是前面的预处理可以拆边用LCT做,后面的查询用可持久化线段树。时间复杂度 O(nlogn) ,在线,常数略大。
离线的话大概是倒序维护标号最大生成树就可以了吧。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a , _ = b ; i <= _ ; i ++)
#define per(i,a,b) for(int i = a , _ = b ; i >= _ ; i --)
#define cr(x) memset(x , 0 , sizeof x)
#define lc ch[u][0]
#define rc ch[u][1]
#define maxn 200007
#define maxm 400007
#define maxs 4000007
inline int rd() {
char c = getchar();
while (!isdigit(c)) c = getchar() ; int x = c - '0';
while (isdigit(c = getchar())) x = x * 10 + c - '0';
return x;
}
inline void upmin(int&a , int b) { if (a > b) a = b; }
typedef int arr[maxn];
typedef int lct[maxm];
typedef int seg[maxs];
const int inf = 0x7fffffff;
arr x , y , pre , rt;
lct val , rv , mn , fa , sta;
int ch[maxm][2];
int n , m , q , tot , top , Type;
void input() {
n = rd() , m = rd() , q = rd() , Type = rd();
rep (i , 1 , m) x[i] = rd() , y[i] = rd();
}
inline bool isrt(int u) {
return (ch[fa[u]][0] != u) && (ch[fa[u]][1] != u);
}
inline void rev(int u) {
if (!u) return;
std::swap(lc , rc);
rv[u] ^= 1;
}
inline void mt(int u) {
if (!u) return;
mn[u] = val[u];
if (lc) upmin(mn[u] , mn[lc]);
if (rc) upmin(mn[u] , mn[rc]);
}
inline void ps(int u) {
if (!u || !rv[u]) return;
rev(lc) , rev(rc);
rv[u] = 0;
}
inline void clear(int u) {
for (sta[top ++] = u;u;u = fa[u]) sta[top ++] = fa[u];
for (;top;) ps(sta[-- top]);
}
inline void rot(int u) {
int f = fa[u] , g = fa[f];
int l = (ch[f][1] == u) , r = l ^ 1;
if (!isrt(f)) ch[g][ch[g][1] == f] = u;
fa[u] = g , fa[f] = u; if (ch[u][r]) fa[ch[u][r]] = f;
ch[f][l] = ch[u][r] , ch[u][r] = f;
mt(f) , mt(u);
}
inline void splay(int u) {
for (clear(u);!isrt(u);rot(u)) {
int f = fa[u] , g = fa[f];
if (!isrt(f))
rot(((ch[f][1] == u) ^ (ch[g][1] == f)) ? u : f);
}
mt(u);
}
inline void access(int u) {
int v = u;
for (int t = 0;u;t = u , u = fa[u])
splay(u) , rc = t;
splay(v);
}
inline void mkrt(int u) {
access(u);
rev(u);
}
inline void cut(int u , int v) {
mkrt(v) , access(u);
fa[lc] = 0 , lc = 0;
mt(u);
}
inline void link(int u , int v) {
mkrt(u) , fa[u] = v;
access(u);
}
inline bool connected(int u , int v) {
mkrt(v) , access(u);
while (lc) u = lc;
if (u != v) return 0;
return 1;
}
inline int GetMin(int u , int v) {
mkrt(v) , access(u);
return mn[u];
}
#undef lc
#undef rc
seg lc , rc , sz;
void update(int pr , int&nr , int l , int r , int p) {
if (!nr) nr = ++ tot;
sz[nr] = sz[pr] + 1;
if (l == r) return;
int m = l + r >> 1;
if (p <= m)
rc[nr] = rc[pr] , update(lc[pr] , lc[nr] , l , m , p);
else
lc[nr] = lc[pr] , update(rc[pr] , rc[nr] , m + 1 , r , p);
}
int query(int pr , int nr , int l , int r , int p) {
if (r <= p) return sz[nr] - sz[pr];
if (l > p) return 0;
int m = l + r >> 1;
int ret = query(lc[pr] , lc[nr] , l , m , p);
if (p > m) ret += query(rc[pr] , rc[nr] , m + 1 , r , p);
return ret;
}
void init() {
rep (i , 1 , n) val[i] = mn[i] = inf;
rep (i , 1 , m) {
val[i + n] = mn[i + n] = i;
int u = x[i] , v = y[i];
if (u == v) {
pre[i] = -1;
continue;
}
if (connected(u , v)) {
int w = GetMin(u , v);
cut(x[w] , w + n);
cut(w + n , y[w]);
pre[i] = w;
}
link(u , i + n);
link(i + n , v);
}
rep (i , 1 , m)
update(rt[i - 1] , rt[i] , 0 , m , pre[i] == -1 ? m : pre[i]);
}
void solve() {
init();
int ans = 0;
rep (i , 1 , q) {
int l = rd() , r = rd();
if (Type)
l ^= ans , r ^= ans;
ans = query(rt[l - 1] , rt[r] , 0 , m , l - 1);
printf("%d\n" , ans = n - ans);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("data.txt" , "r" , stdin);
freopen("data.out" , "w" , stdout);
#endif
input();
solve();
return 0;
}