Luogu P5360 - [SDOI2019] world map (minimum spanning tree + virtual tree)

Posted by jjjamie on Sun, 30 Jan 2022 03:58:08 +0100

Luogu problem plane portal

Good question.

First of all, seeing that the restriction of an interval is removed, we naturally think of the idea of merging the left and right half information after running MST for the pre suffix, so the problem is how to maintain the minimum spanning tree of the pre suffix.

It is obvious that the complexity \ (nm^2\log n \) directly cannot be passed. At first glance, it seems that it also needs strange data structures such as persistent LCT / tree array set LCT to be optimized, which looks very difficult. But don't forget that we haven't used the property that "a graph is a grid graph". We notice that the number of columns \ (m \) is large, but the number of rows \ (n \) is small, so we will try our best to incline the complexity to \ (n \). It can be found that when we add and expand a column \ (I \), the new edges will only be connected between \ (\ mathcal O(n) \) points, that is, all points shaped like \ ((j,i-1),1\le j\le m \). According to the theory of LCT maintaining the minimum spanning tree, when we add a new edge \ (E=(u,v,w) \), the change of the minimum spanning tree can be shown as taking out the edge \ (E_0 \) with the largest weight on the \ (u,v \) path. If the weight \ (> W \) of \ (E_0 \) is deleted, add \ (E \) or nothing will be done. In other words, in this round of expansion, only the edge with the largest weight on the two paths of these \ (n \) key points may be deleted in this round of expansion, and there are only \ (\ mathcal O(n) \) such edges at most, because if we build a virtual tree for \ (n \) key points, such edges must be the edge with the largest weight on a chain on the virtual tree, The maximum number of edges on the virtual tree is \ (2n-2 \).

Therefore, we consider not recording the edge set of the whole minimum spanning tree, but only the set composed of these "key edges". For the remaining edges on MST, no matter how we expand them, they will certainly be on MST. Therefore, we only need to record the sum of their edge weights. Directly recording the numbers of these edges in the original graph will lead to an error in kruskal's result. Therefore, we cannot directly record the numbers of these edges in the original graph. The improvement method is to find the virtual tree of \ ((1,1),(2,1),(3,1),\cdots,(n,1),(1,i-1),(2,i-1),\cdots,(n,i-1) \) on \ (1 \ SIM, i-1 \), Then re label the \ (\ mathcal O(n) \) points on the virtual tree and find the edge with the largest weight for each chain on the virtual tree. When adding a new column, we will run kruskal together with the original \ (\ mathcal O(n) \) edge, build the minimum spanning tree of these \ (9n \) points, and then build the virtual tree with \ ((1,1),(2,1),\cdots,(n,1),(1,i),(2,i),\cdots,(n,i) \) as the key point, The process of merging two MSTS can be realized by finding the edge with the largest weight on each chain as a new key edge set.

Time complexity \ (n(m+q)\log n \), some unclear places can be understood by reading the code.

const int MAXN = 100;
const int MAXM = 1e4;
const int MAXC = MAXN << 4;
int n, m, hor[MAXN + 5][MAXM + 5], vert[MAXN + 5][MAXM + 5];
u32 SA, SB, SC; int lim;
int getweight() {
	SA ^= SA << 16; SA ^= SA >> 5; SA ^= SA << 1;
	unsigned int t = SA;
	SA = SB; SB = SC; SC ^= t ^ SA;
	return SC % lim + 1;
}
struct edge {
	int u, v, w;
	edge(int _u = 0, int _v = 0, int _w = 0): u(_u), v(_v), w(_w) {}
	bool operator < (const edge &rhs) {return w < rhs.w;}
};
struct dsu {
	int f[MAXC + 5];
	void init() {memset(f, 0, sizeof(f));}
	int find(int x) {return (!f[x]) ? x : f[x] = find(f[x]);}
	bool merge(int x, int y) {x = find(x); y = find(y); return (x == y) ? 0 : (f[x] = y, 1);}
} F;
struct graph {
	int hd[MAXC + 5], nxt[MAXC * 2 + 5], to[MAXC * 2 + 5], val[MAXC * 2 + 5], ec = 0;
	void init() {memset(hd, 0, sizeof(hd)); ec = 0;}
	void adde(int u, int v, int w) {
		to[++ec] = v; val[ec] = w; nxt[ec] = hd[u]; hd[u] = ec;
		to[++ec] = u; val[ec] = w; nxt[ec] = hd[v]; hd[v] = ec;
	}
} G;
bool is[MAXC + 5], ont[MAXC + 5];
struct MST {
	vector<edge> E; int tot;
	ll static_sum; // sum of static edges
	void init(vector<int> w) {
		tot = n;
		for (int i = 0; i < w.size(); i++)
			E.pb(edge(i + 1, i + 2, w[i]));
	}
	ll query() {
		ll sum = static_sum;
		for (int i = 0; i < E.size(); i++) sum += E[i].w;
		return sum;
	}
} pre[MAXM + 5], suf[MAXM + 5];
int fa[MAXC + 5], faw[MAXC + 5];
void dfs_init(int x, int f) {
	fa[x] = f;
	for (int e = G.hd[x]; e; e = G.nxt[e]) {
		int y = G.to[e], z = G.val[e]; if (y == f) continue;
		faw[y] = z; dfs_init(y, x);
	}
}
int id[MAXC + 5], idcnt = 0;
vector<pii> te;
int dfs_build(int x, int f) {
	int V = 0, two = 0; ont[x] = is[x];
	for (int e = G.hd[x]; e; e = G.nxt[e]) {
		int y = G.to[e]; if (y == f) continue;
		int z = dfs_build(y, x);
		if (z) {
			if (V) two = 1, te.pb(mp(x, z));
			else V = z;
		}
	}
	if (!V) return (is[x]) ? x : 0;
	else {
		ont[x] = 1;
		if (two) return is[x] = 1, te.pb(mp(x, V)), x;
		else {
			if (is[x]) return te.pb(mp(x, V)), x;
			else return V;
		}
	}
}
int qrymx(int u, int v) {
	int mx = 0;
	while (v ^ u) chkmax(mx, faw[v]), v = fa[v];
	return mx;
}
MST merge(MST &a, MST &b, vector<int> w) {
	vector<edge> ve;
	MST c; c.tot = a.tot + b.tot;
	for (int i = 0; i < a.E.size(); i++) ve.pb(a.E[i]);
	for (int i = 0; i < b.E.size(); i++) ve.pb(edge(b.E[i].u + a.tot, b.E[i].v + a.tot, b.E[i].w));
	for (int i = 1; i <= n; i++) ve.pb(edge(a.tot - n + i, a.tot + i, w[i - 1]));
	F.init(); G.init(); ll esum = 0; sort(ve.begin(), ve.end());
	for (int i = 0; i < ve.size(); i++) if (F.merge(ve[i].u, ve[i].v))
		G.adde(ve[i].u, ve[i].v, ve[i].w), esum += ve[i].w;
	memset(is, 0, sizeof(is)); memset(ont, 0, sizeof(ont));
	for (int i = 1; i <= c.tot; i++) is[i] = (i <= n || i > c.tot - n);
	dfs_init(1, 0); te.clear(); dfs_build(1, 0);
	idcnt = 0; for (int i = 1; i <= c.tot; i++) if (ont[i]) id[i] = ++idcnt;
	for (pii p : te) c.E.pb(edge(id[p.fi], id[p.se], qrymx(p.fi, p.se)));
	for (int i = 0; i < c.E.size(); i++) esum -= c.E[i].w;
	c.static_sum = a.static_sum + b.static_sum + esum;
	c.tot = idcnt;
	return c;
}
int main() {
	scanf("%d%d%u%u%u%d", &n, &m, &SA, &SB, &SC, &lim);
	for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) hor[i][j] = getweight();
	for (int i = 1; i < n; i++) for (int j = 1; j <= m; j++) vert[i][j] = getweight();
//	printf("hor:\n");
//	for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++)
//		printf("%d%c", hor[i][j], " \n"[j == m]);
//	printf("vert:\n");
//	for (int i = 1; i < n; i++) for (int j = 1; j <= m; j++)
//		printf("%d%c", vert[i][j], " \n"[j == m]);
	for (int j = 1; j <= m; j++) {
		vector<int> vec;
		for (int i = 1; i < n; i++) vec.pb(vert[i][j]);
		pre[j].init(vec); suf[j].init(vec);
	}
	for (int i = 2; i <= m; i++) {
		vector<int> vec;
		for (int j = 1; j <= n; j++) vec.pb(hor[j][i - 1]);
		pre[i] = merge(pre[i - 1], pre[i], vec);
	}
	for (int i = m - 1; i; i--) {
		vector<int> vec;
		for (int j = 1; j <= n; j++) vec.pb(hor[j][i]);
		suf[i] = merge(suf[i], suf[i + 1], vec);
	}
//	printf("pre:\n");
//	for (int i = 1; i <= m; i++) {
//		printf("[1, %d]:\n", i);
//		for (int j = 0; j < pre[i].E.size(); j++)
//			printf("%d %d %d\n", pre[i].E[j].u, pre[i].E[j].v, pre[i].E[j].w);
//		printf("weight of MST: %lld\n", pre[i].query());
//	}
	int qu; scanf("%d", &qu);
	while (qu--) {
		int l, r; scanf("%d%d", &l, &r); vector<int> vec;
		for (int j = 1; j <= n; j++) vec.pb(hor[j][m]);
		printf("%lld\n", merge(suf[r + 1], pre[l - 1], vec).query());
	}
	return 0;
}
/*
6 5 998244353 1004535809 1000000007 5
6
2 2
2 3
2 4
3 3
3 4
4 4
*/