Description:
1<=n<=10^6,1<=q<=200000
题解:
暴力可以用按秩合并并查集,这个数据结构支持删除。
对于正解,我们需要把l->r条边一次加进并查集,对于每一条边,它有贡献,就是加入它的时候,它会合并两个不同的集合。
反过来,它没有贡献,就是它所连着的两端本来就在一个集合里。
假设我们按1->r的顺序加入每一条边,当加入一条边时,设它是x,y,设z为x到y的路径上编号最小的边(x,y不在一个集合里是0)的编号,那么如果有询问包含这条边,且左端点小于等于z,那么当前这条边就是没有贡献的,否则它就是有贡献。
于是用lct维护一棵最大生成树,就可以求出z,再用主席树随便维护一下就行了。
Code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = 1000000, M = 200005;
int n, q, type, tt, u, v, l, r, ans, m;
int t[N + M][2], fa[N + M], pf[N + M], rev[N + M], mi[N + M], dd[N + M];
int g[N + M], tot;
struct tree{
int l, r, s;
}d[N * 10];
struct edge {
int u, v, x, y, z;
}e[N + M];
int last[N + M];
int lr(int x) {return t[fa[x]][1] == x;}
void chan(int x) {if(x) swap(t[x][0], t[x][1]), rev[x] ^= 1;}
void down(int x) {if(x && rev[x]) chan(t[x][0]), chan(t[x][1]), rev[x] = 0;}
void update(int x) {
if(!x) return;
mi[x] = min(mi[t[x][0]], mi[t[x][1]]);
if(x > N) mi[x] = min(mi[x], x - N);
}
void xc(int x) {
for(; x; x = fa[x]) dd[++ dd[0]] = x;
for(; dd[0]; dd[0] --) down(dd[dd[0]]);
}
void rotate(int x) {
int y = fa[x], k = lr(x);
t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
t[x][!k] = y; fa[y] = x; pf[x] = pf[y];
update(y); update(x);
}
void splay(int x, int y) {
xc(x);
while(fa[x] != y) {
if(fa[fa[x]] != y)
if(lr(x) == lr(fa[x])) rotate(fa[x]); else rotate(x);
rotate(x);
}
}
void dg(int x) {
if(x == 0) return;
down(x);
printf("%d %d %d %d tree\n", x, mi[x], t[x][0], t[x][1]);
dg(t[x][0]); dg(t[x][1]);
}
void access(int x) {
int y = 0;
while(x) {
splay(x, 0); fa[t[x][1]] = 0; pf[t[x][1]] = x;
t[x][1] = y; fa[y] = x; pf[y] = 0;
update(x); y = x; x = pf[x];
}
}
void makeroot(int x) {
access(x); splay(x, 0); chan(x);
}
void link(int x, int y) {
makeroot(x); pf[x] = y; access(x);
}
void cut(int x, int y) {
makeroot(x); access(y); splay(y, 0);
t[y][0] = fa[x] = pf[x] = 0;
update(y);
}
int find_z(int x) {
down(x);
return t[x][0] == 0 ? x : find_z(t[x][0]);
}
int pd(int x, int y) {
makeroot(x); access(y); splay(y, 0);
return find_z(y) == x;
}
void change(int i, int x, int y, int l, int r) {
if(x == y) {
d[i].s += r;
return;
}
int m = (x + y) >> 1;
d[++ tot] = d[d[i].l], d[i].l = tot;
d[++ tot] = d[d[i].r], d[i].r = tot;
if(l <= m) change(d[i].l, x, m, l, r); else change(d[i].r, m + 1, y, l, r);
d[i].s = d[d[i].l].s + d[d[i].r].s;
}
int find(int i, int x, int y, int l, int r) {
if(i == 0) return 0;
if(x == l && y == r) return d[i].s;
int m = (x + y) >> 1;
if(r <= m) return find(d[i].l, x, m, l, r); else
if(l > m) return find(d[i].r, m + 1, y, l, r); else
return find(d[i].l, x, m, l, m) + find(d[i].r, m + 1, y, m + 1, r);
}
const int Mm = 200000;
void xiu(int m, int last) {
g[m] = ++ tot; d[g[m]] = d[g[m - 1]];
change(g[m], 0, Mm, last, 1);
}
int qiu(int l, int r) {
return find(g[r], 0, Mm, 0, l - 1) - find(g[l - 1], 0, Mm, 0, l - 1);
}
int main() {
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
scanf("%d %d %d", &n, &q, &type);
fo(i, 0, N) mi[i] = 1e9;
g[0] = tot = 1;
fo(Q, 1, q) {
scanf("%d", &tt);
if(tt == 1) {
scanf("%d %d", &u, &v);
if(type) u = (u ^ ans) % n + 1;
if(type) v = (v ^ ans) % n + 1;
e[++ m].u = u, e[m].v = v;
if(u == v) {
last[m] = m; xiu(m, m);
continue;
}
if(pd(u, v)) {
makeroot(u); access(v); splay(v, 0);
int z = mi[v];
e[m].x = e[z].u; e[m].y = e[z].v; e[m].z = z;
cut(e[m].x, e[m].z + N); cut(e[m].y, e[m].z + N);
link(u, m + N); link(v, m + N);
last[m] = z; xiu(m, z);
} else {
link(u, m + N); link(v, m + N);
last[m] = 0; xiu(m, 0);
}
} else
if(tt == 2) {
if(e[m].u == e[m].v) {
m --; continue;
}
if(last[m]) {
cut(e[m].u, m + N); cut(e[m].v, m + N);
link(e[m].x, e[m].z + N); link(e[m].y, e[m].z + N);
} else {
cut(e[m].u, m + N); cut(e[m].v, m + N);
}
m --;
} else {
scanf("%d %d", &l, &r);
if(type) l = (l ^ ans) % m + 1;
if(type) r = (r ^ ans) % m + 1;
if(l > r) swap(l, r);
ans = n - qiu(l, r);
printf("%d\n", ans);
}
}
}