[Cnoi2019]须臾幻境(LCT 维护最大生成树)

这曾今有一个凄婉哀伤的故事,但是被出题人删档弄丢了。

题目名字这么高尚,背景你却给我咕了??

Description

点数 \(n\) ,边数 \(m\) ,询问 \(q\) 次,求一段区间内的边组成的残图的连通块个数。

\(n,\ q \leq 10 ^ 5,\ m \leq 2 \cdot 10 ^ 5\)

Analysis

首先我们要知道对于一个残图,它的点数减生成森林的边数就是连通块的个数。

因为它本身是作为图为存在的,所以即需要维护区间生成树(森林)。

Solution

又是区间,又是生成树什么跟图扯上关系的要求,看起来就头大。

可以考虑用 \(\text{LCT}\) 维护生成树。

但是生成树可能会有很多种,我们就需要所有边编号尽可能靠近那个区间。

怎么办。

我们考虑在加边的时候,假如当前两个点原先已经联通,我们尝试去找连通块上最早加进来的边,并把他删掉。

这样就能保证在图的连通性不变的情况下,让生成树上的边尽可能靠近右边界。

这样我们可以在 \(\text{LCT}\) 里面维护连通块内编号最小的地方。

众所周知 \(\text{LCT}\) 是维护的点权,所以要把边化点。不过比较典的套路,可以直接把边也当作点塞到 \(\text{LCT}\) 里面,就像 \(\text{kruskal}\) 重构树那样连边。

所这样只需要把边的编号当作点权,其他图上的点设成 \(\infty\) ,维护最小值就行了。

那如果我们想要随时知道前 r 条边有多少条生成树边,把这些甩到主席树上就行了。但是注意这玩意相当于能查询前 r 条边有多少条存在于生成树上。

因为我们是尽量让所有树边编号尽可能大,所以就类似二位数点 \(f_r - f_{l - 1}\) 那样做个差就知道前 \(r\) 条边最多有多少条在区间 \([l,\ r]\) 内。

不得不说 \(\text{LCT}\) 有些操作看起来是真的厉害(

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

Code

Code

/*

*/
#include 
using namespace std;

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair pii;
#define fi first
#define se second
#define mp std::make_pair

const int mod = 998244353;
template 
inline int M(A x) {return x;}
template 
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}

#define mi(x) (x >= mod) && (x -= mod)
#define ad(x) (x < 0) && (x += mod)

const int N = 2e5 + 10, P = 3e5 + 10, INF = 1e9;

int n, m, q, op, rt[N], del[N];
struct edge {int u, v;} e[N];

struct LCT {
	#define ls(x) ch[x][0]
	#define rs(x) ch[x][1]

	int ch[P][2], fa[P], mn[P], va[P], rev[P], st[P], tp;

	#define pd(x) (!(rs(fa[x]) ^ x))
	#define isrt(x) (ls(fa[x]) ^ x && rs(fa[x]) ^ x)

	inline void pushup(int x) {mn[x] = min(va[x], min(mn[ls(x)], mn[rs(x)]));}

	#define prev(x) (rev[x] ^= 1, ls(x) ^= rs(x) ^= ls(x) ^= rs(x))

	inline void pushdown(int x) {
		rev[x] && (
			ls(x) && (prev(ls(x))), 
			rs(x) && (prev(rs(x))), rev[x] = 0
		);
	}

	inline void rotate(int x) {
		int d = fa[x], t = pd(x);
		ch[d][t] = ch[x][t ^ 1]; fa[ch[x][t ^ 1]] = d;

		!isrt(d) && (ch[fa[d]][pd(d)] = x);

		fa[x] = fa[d]; ch[x][t ^ 1] = d; fa[d] = x;
		pushup(d); pushup(x);
	}

	inline void push(int x) {
		for (; !isrt(x); x = fa[x]) st[++tp] = x;
		st[++tp] = x;
		while (tp) pushdown(st[tp--]);
	}

	inline void splay(int x) {
		for (push(x); !isrt(x); rotate(x)) {
			!isrt(fa[x]) && (rotate((pd(x) ^ pd(fa[x])) ? x : fa[x]), 1);
		}
		pushup(x);
	}

	inline void access(int x) {
		for (int y = 0; x; y = x, x = fa[x]) splay(x), rs(x) = y, pushup(x);
	}

	inline void makert(int x) {access(x); splay(x); prev(x);}

	inline void split(int x, int y) {makert(x); access(y); splay(y);}

	inline int firt(int x) {
		access(x); splay(x);
		for (; ls(x); pushdown(x), x = ls(x));
		splay(x); return x;
	}

	inline void link(int x, int y) {makert(x); fa[x] = y;}

	inline void cut(int x, int y) {split(x, y); ls(y) = fa[x] = 0; pushup(y);}

	#undef ls
	#undef rs

	#undef pd
	#undef isrt
} L;

struct PresiTree {
	struct mdzz {int su, ch[2];} tr[N << 5];
	int cnt;

	#define ls(x) tr[x].ch[0]
	#define rs(x) tr[x].ch[1]

	inline void modify(int &x, int l, int r, int k, int p) {
		x = ++cnt; tr[x] = tr[p]; ++tr[x].su;
		if (l == r) return ;

		int mid = (l + r) >> 1;
		if (k <= mid) modify(ls(x), l, mid, k, ls(p));
		else modify(rs(x), mid + 1, r, k, rs(p));
	}

	inline int query(int x, int l, int r, int L, int R) {
		if (!x) return 0;
		if (L <= l && r <= R) return tr[x].su;

		int mid = (l + r) >> 1, ret = 0;
		if (L <= mid) ret += query(ls(x), l, mid, L, R);
		if (R > mid) ret += query(rs(x), mid + 1, r, L, R);
		return ret;
	}

	#undef ls
	#undef rs
} Pt;

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

	std::cin >> n >> m >> q >> op;
	for (int i = 0; i <= n; ++i) L.va[i] = L.mn[i] = INF;
	for (int i = 1; i <= m; ++i) L.va[i + n] = L.mn[i + n] = i;

	for (int i = 1, j; i <= m; ++i) {
		std::cin >> e[i].u >> e[i].v;
		int x = e[i].u, y = e[i].v;

		if (!(x ^ y)) {del[i] = i; continue;}

		L.firt(x) == L.firt(y) && (
			L.split(x, y), j = L.mn[y], 
			L.cut(e[j].u, j + n), L.cut(e[j].v, j + n), del[j] = i
		);
		L.link(x, i + n); L.link(y, i + n);
	}

	for (int i = 1; i <= m; ++i) {
		if (!del[i]) del[i] = m + 1;
		Pt.modify(rt[i], 1, m + 1, del[i], rt[i - 1]);
	}

	int ans = 0;

	while (q--) {
		int l, r; std::cin >> l >> r;
		op && (l = (l + ans) % m + 1, r = (r + ans) % m + 1);
		l > r && (l ^= r ^= l ^= r);

		int a = Pt.query(rt[r], 1, m + 1, r + 1, m + 1);
		int b = Pt.query(rt[l - 1], 1, m + 1, r + 1, m + 1);
		ans = n - a + b;

		std::cout << ans << "\n";
	}

	return 0;
}


posted @ 2022-08-06 10:56  Illusory_dimes  阅读(72)  评论(0编辑  收藏  举报