AtCoder Beginner Contest 394(G - Dense Buildings 整体二分)

前言:感觉题解的整体二分怪怪的,好像不是很板啊,故而修改成比较板的整体二分形式, 这里不教整体二分捏

首先,为什么可以整体二分:容易观察其答案具有二分性
其次多个询问,符合整体二分性质

考虑对于某个中间层mid,怎么判两点是否连通
这个很容易,可以用并查集把mid及以上的点连接在一起

然后根据整体二分模板,一套

点击查看代码
auto dfs = [&] (auto && self, int l, int r, vector<node> q) -> void {
	if (l > r || q.size() == 0) return;
	if (l == r) {
		for (auto i : q) {
			ans[i.id] = i.z + i.y - 2 * min({l, i.z, i.y});
		}
		return;
	}
	DSU dsu(n * m + 1);
	int mid = l + r >> 1;
	vector<node> L, R;
	for (int i = mid + 1; i <= r; i ++) {
		for (auto [u, v] : g[i]) {
			dsu.merge(u, v);
		}
	}

	for (auto &i : q) {
		if (dsu.same(i.a, i.c)) {
			R.push_back(i);
		}
		else L.push_back(i);
	}
	self(self, l, mid, L);
	self(self, mid + 1, r, R);
};

然后发现样例都过不了,发现这里出了问题

点击查看代码
	for (int i = mid + 1; i <= r; i ++) {
		for (auto [u, v] : g[i]) {
			dsu.merge(u, v);
		}
	}
这里只能在区间mid + 1 - r,但本意应该是大于mid的所有
点击查看代码
	for (int i = mid + 1; i < N; i ++) {
		for (auto [u, v] : g[i]) {
			dsu.merge(u, v);
		}
	}
固然正确性达到保证,但是我们发现复杂度是N*nlong

但是我们发现对于左区间的可以保留并查集结果,对于大于右区间可以削去贡献,然后就很容易想到可撤销并查集

神奇的代码
#include<bits/stdc++.h>

using namespace std;
using i128 = __int128;
using u64 = unsigned long long;


#define cc(x) cout << fixed << setprecision(x)
#define int long long

const int N = 1e6 + 10;
struct DSU {
	std::vector<int> f, siz, s;
	int tot;
	DSU() {}
	DSU(int n) {
		init(n);
	}

	void init(int n) {
		f.resize(n);
		s.resize(n);
		tot = 0;
		std::iota(f.begin(), f.end(), 0);
		siz.assign(n, 1);
	}

    int find(int x) {
        return x == f[x] ? x : find(f[x]);
    }

	bool same(int x, int y) {
		return find(x) == find(y);
	}

	bool merge(int x, int y) {
		x = find(x), y = find(y);
		if (x == y) return 0;
		if (siz[x] < siz[y]) swap(x, y);
		s[++tot] = y, f[y] = x, siz[x] += siz[y];
		return 1;
	}
	void del(int t) {
		while (tot > t) {
			int y = s[tot--]; siz[f[y]] -= siz[y], f[y] = y;
		}
	}
	int size(int x) {
		return siz[find(x)];
	}
};

vector<pair<int, int>> g[N];

struct node {
	int a, y, c, z, id;
};

void solve() {
	int n, m;
	cin >> n >> m;

	auto idx = [&](int i, int j) {
		return (i - 1) * m + j;
	};
	vector f(n + 1, vector<int>(m + 1));
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			cin >> f[i][j];
		}
	}
	DSU dsu(n * m + 1);
	for (int i = 1; i < n; i++)
		for (int j = 1; j <= m; j++)
			g[min(f[i][j], f[i + 1][j])].push_back({idx(i, j), idx(i + 1, j)});
	for (int i = 1; i <= n; i++)
		for (int j = 1; j < m; j++)
			g[min(f[i][j], f[i][j + 1])].push_back({idx(i, j), idx(i, j + 1)});

	int q;
	cin >> q;
	vector<int> ans(q + 1);
	vector<node> qr(q);
	for (int i = 0; i < q; i ++) {
		int a, b, y, c, d, z;
		cin >> a >> b >> y >> c >> d >> z;
		qr[i].a = idx(a, b);
		qr[i].y = y;
		qr[i].c = idx(c, d);
		qr[i].z = z;
		qr[i].id = i + 1;
	}


	auto dfs = [&] (auto && self, int l, int r, vector<node> q) -> void {
		if (l > r || q.size() == 0) return;
		if (l == r) {
			for (auto i : q) {
				ans[i.id] = i.z + i.y - 2 * min({l, i.z, i.y});
			}
			return;
		}
		int tot = dsu.tot;
		int mid = l + r >> 1;
		vector<node> L, R;
		for (int i = mid + 1; i <= r; i ++) {
			for (auto [u, v] : g[i]) {
				dsu.merge(u, v);
			}
		}

		for (auto &i : q) {
			if (dsu.same(i.a, i.c)) {
				R.push_back(i);
			}
			else L.push_back(i);
		}
		self(self, l, mid, L);
		//
		dsu.del(tot);
		self(self, mid + 1, r, R);
	};
	dfs(dfs, 1, N - 10, qr);

	
	for (int i = 1; i <= q; i ++) {
		cout << ans[i] << '\n';
	}

}

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

	int T = 1;
	// cin >> T;

	while (T--) {
		solve();
	}

	return 0;
}

跟倍增复杂度差不多,甚至还快上一点
有点懒,估计没人看了吧

posted @ 2025-02-28 20:29  不o凡  阅读(34)  评论(0)    收藏  举报