题目链接:BZOJ3514
分析
1. 首先,对于询问
[l..r]
,我们用
[1..r]
的边,设边权为边的标号,建立一颗最大生成树;那么,点数减去树上的编号为
[l..r]
的边的数量即为答案。
2. 那么,依次加边,LCT维护最大生成树,用主席树记录加完
i
这条边后,有哪些边还在树上。
3. 询问时直接询问第
上代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m, q, type;
inline int read() {
char ch = getchar();
register int ans = 0, neg = 1;
for (; !isdigit(ch); ch = getchar())
if (ch == '-') neg = -1;
for (; isdigit(ch); ch = getchar())
ans = ans * 10 + ch - '0';
return ans * neg;
}
namespace LCT {
const int M = 4e5 + 10;
int cnt;
bool rev[M];
int fa[M], ch[M][2], val[M], minp[M];
#define lc(a) (ch[a][0])
#define rc(a) (ch[a][1])
#define csk(a) (rc(fa[a]) == a)
#define makeLink(a, b, c) (fa[ch[a][c] = b] = a)
#define isRoot(a) (lc(fa[a]) != a && rc(fa[a]) != a)
void init() {
cnt = n;
for (int i = 0; i <= n; ++i)
val[i] = INF, minp[i] = i;
}
inline void update(int a) {
minp[a] = a;
if (val[minp[lc(a)]] < val[minp[a]]) minp[a] = minp[lc(a)];
if (val[minp[rc(a)]] < val[minp[a]]) minp[a] = minp[rc(a)];
}
inline void reverse(int a) {
rev[a] ^= 1, swap(lc(a), rc(a));
}
inline void pushDown(int a) {
if (rev[a]) {
rev[a] = false;
reverse(lc(a)), reverse(rc(a));
}
}
void pushDownTo(int a) {
if (!isRoot(a))
pushDownTo(fa[a]);
pushDown(a);
}
inline void rotate(int a) {
int c = csk(a), f = fa[a];
makeLink(f, ch[a][c ^ 1], c);
if (isRoot(f)) fa[a] = fa[f];
else makeLink(fa[f], a, csk(f));
makeLink(a, f, c ^ 1);
update(f), update(a);
}
inline void splay(int a) {
pushDownTo(a);
while (!isRoot(a)) {
if (!isRoot(fa[a])) {
if (csk(a) == csk(fa[a])) rotate(fa[a]);
else rotate(a);
} rotate(a);
}
}
inline void access(int a) {
splay(a), rc(a) = 0, update(a);
for (int i = fa[a]; i; i = fa[a = i])
splay(i), rc(i) = a, update(i);
}
inline void makeRoot(int a) {
access(a), splay(a), reverse(a);
}
inline void link(int a, int b) {
makeRoot(a), fa[a] = b;
}
inline void cut(int a, int b) {
makeRoot(a), access(b), splay(b);
lc(b) = fa[a] = 0, update(b);
}
inline int getMin(int a, int b) {
return makeRoot(a), access(b), splay(b), val[minp[b]];
}
inline void delMin(int a, int b) {
makeRoot(a), access(b), splay(b);
int tmp = minp[b];
cut(a, tmp), cut(b, tmp);
}
inline void addLink(int a, int b, int c) {
int tmp = ++cnt; val[tmp] = c;
update(tmp), link(a, tmp), link(b, tmp);
}
}
namespace CT {
const int M = 75e5 + 10;
int T[N], cnt;
int lc[M], rc[M], tot[M];
int modify(int a, int l, int r, int p, int c) {
int now = ++cnt; tot[now] = tot[a] + c;
if (l == r) return now;
int mid = (l + r) >> 1;
if (p <= mid) rc[now] = rc[a], lc[now] = modify(lc[a], l, mid, p, c);
else lc[now] = lc[a], rc[now] = modify(rc[a], mid + 1, r, p, c);
return now;
}
int query(int a, int l, int r, int ll, int rr) {
if (ll > rr) return 0;
if (l == ll && r == rr) return tot[a];
int mid = (l + r) >> 1;
if (rr <= mid) return query(lc[a], l, mid, ll, rr);
else if (ll > mid) return query(rc[a], mid + 1, r, ll, rr);
return query(lc[a], l, mid, ll, mid) + query(rc[a], mid + 1, r, mid + 1, rr);
}
}
int fa[N];
int findRoot(int a) {
return fa[a] == a ? a : fa[a] = findRoot(fa[a]);
}
int main() {
n = read(), m = read(), q = read(), type = read();
LCT::init();
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
int a = read(), b = read();
CT::T[i] = CT::T[i - 1];
if (a == b) continue;
if (findRoot(a) == findRoot(b)) {
CT::T[i] = CT::modify(CT::T[i], 1, m, i, 1);
CT::T[i] = CT::modify(CT::T[i], 1, m, LCT::getMin(a, b), -1);
LCT::delMin(a, b), LCT::addLink(a, b, i);
} else {
CT::T[i] = CT::modify(CT::T[i], 1, m, i, 1);
LCT::addLink(a, b, i), fa[findRoot(a)] = findRoot(b);
}
}
int ans = 0;
for (int i = 1; i <= q; ++i) {
int l = read(), r = read();
l = l ^ (ans * type), r = r ^ (ans * type);
printf("%d\n", ans = n - CT::query(CT::T[r], 1, m, l , r));
}
return 0;
}
以上