Luogu P5385 [Cnoi2019]须臾幻境

题目描述

https://www.luogu.com.cn/problem/P5385

简要题意:给定一个 $n$ 个点 $m$ 条边的无向图,现在有 $q$ 次询问,每次询问给定一个区间 $[l,r]$,求只包含编号在 $[l,r]$ 内的边的连通块的数量,强制在线

$n,q\le 10^5,m\le 2\times 10^5$

Solution

不妨先考虑不强制在线的情况,我们使用扫描线同时用线段树维护左端点的答案,那么如果我们加入编号为 $r$ 的$(u,v)$ 这条边,它会使 左端点在 $[l,r]$ 的答案减一,这个 $l$ 是从 $r-1$ 向前扫不断加边直至 $u$ 和 $v$ 连通

我们发现这个东西是可以用 $LCT$ 维护的,我们只需要用 $LCT$ 维护最大生成树即可求这个东西,每次不断替换环上编号最小的边

强制在线的话,我们只需要将扫描线的过程可持久化一下即可

时间复杂度 $O(n\log n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <iostream>
#define maxn 100010
#define maxm 200010
#define INF 1000000000
using namespace std;

int n, m, q, t;

struct Edge {
int u, v, w;
} e[maxm];

namespace LCT {
#define lc T[i].ch[0]
#define rc T[i].ch[1]
struct LinkCutTree {
int v, val, ch[2];
bool rev;
} T[maxn + maxm]; int f[maxn + maxm];
void init() {
e[0].w = INF;
for (int i = 1; i <= m; ++i) T[i + n].val = i;
}
inline int get(int i) {
if (T[f[i]].ch[0] == i) return 0;
if (T[f[i]].ch[1] == i) return 1;
return -1;
}
inline void maintain(int i) {
T[i].v = T[i].val;
if (e[T[lc].v].w < e[T[i].v].w) T[i].v = T[lc].v;
if (e[T[rc].v].w < e[T[i].v].w) T[i].v = T[rc].v;
}
inline void setr(int i) {
if (!i) return ;
T[i].rev ^= 1; swap(lc, rc);
}
inline void push(int i) {
bool &rev = T[i].rev;
if (rev) setr(lc), setr(rc);
rev = 0;
}
inline void rotate(int x) {
int fa = f[x], ffa = f[f[x]], wx = get(x);
if (~get(fa)) T[ffa].ch[T[ffa].ch[1] == fa] = x;
f[x] = ffa; f[fa] = x; f[T[x].ch[wx ^ 1]] = fa;
T[fa].ch[wx] = T[x].ch[wx ^ 1]; T[x].ch[wx ^ 1] = fa;
maintain(fa); maintain(x);
}
void clt(int i) {
static int st[maxn + maxm], top;
st[top = 1] = i;
while (~get(i)) st[++top] = i = f[i];
while (top) push(st[top--]);
}
void Splay(int i) {
clt(i);
for (int fa = f[i]; ~get(i); rotate(i), fa = f[i])
if (~get(fa)) rotate(get(i) == get(fa) ? fa : i);
}
void access(int i) { for (int p = 0; i; i = f[p = i]) Splay(i), rc = p, maintain(i); }
inline void make_rt(int i) { access(i); Splay(i); setr(i); }
inline void split(int x, int y) { make_rt(x); access(y); Splay(y); }
int find_rt(int i) { access(i); Splay(i); while (lc) i = lc; return Splay(i), i; }
inline void link(int x, int y) {
if (find_rt(x) == find_rt(y)) return ;
split(x, y); f[x] = y;
}
inline void cut(int x, int y) {
if (find_rt(x) != find_rt(y)) return ;
split(x, y);
if (T[y].ch[0] == x && !T[x].ch[1])
T[y].ch[0] = f[x] = 0, maintain(y);
}
}

namespace Seg {
#define lc T[i].ch[0]
#define rc T[i].ch[1]
#define Lc T[j].ch[0]
#define Rc T[j].ch[1]
struct Zhuxi {
int v, ch[2];
} T[maxm * 100]; int rt[maxm], top;
void update(int &i, int j, int l, int r, int L, int R, int v) {
if (l > R || r < L) return;
i = ++top; T[i] = T[j];
if (L <= l && r <= R) return T[i].v += v, void();
int m = l + r >> 1;
update(lc, Lc, l, m, L, R, v); update(rc, Rc, m + 1, r, L, R, v);
}
int query(int i, int l, int r, int k) {
if (!i || l == r) return T[i].v;
int m = l + r >> 1;
if (k <= m) return query(lc, l, m, k) + T[i].v;
else return query(rc, m + 1, r, k) + T[i].v;
}
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m >> q >> t;
for (int i = 1; i <= m; ++i) cin >> e[i].u >> e[i].v, e[i].w = i;
LCT::init();
for (int i = 1; i <= m; ++i) {
int u = e[i].u, v = e[i].v; Seg::rt[i] = Seg::rt[i - 1]; if (u == v) continue;
if (LCT::find_rt(u) == LCT::find_rt(v)) {
LCT::split(u, v); int k = LCT::T[v].v;
LCT::cut(e[k].u, k + n), LCT::cut(e[k].v, k + n);
Seg::update(Seg::rt[i], Seg::rt[i], 1, m, k + 1, i, 1);
} else Seg::update(Seg::rt[i], Seg::rt[i], 1, m, 1, i, 1);
LCT::link(u, i + n), LCT::link(v, i + n);
}
for (int i = 1, lans = 0; i <= q; ++i) {
int x, y; cin >> x >> y;
if (t) x = (x + lans) % m + 1, y = (y + lans) % m + 1;
if (x > y) swap(x, y);
cout << (lans = n - Seg::query(Seg::rt[y], 1, m, x)) << "\n";
}
return 0;
}