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);
}
}
点击查看代码
for (int i = mid + 1; i < N; i ++) {
for (auto [u, v] : g[i]) {
dsu.merge(u, v);
}
}
但是我们发现对于左区间的可以保留并查集结果,对于大于右区间可以削去贡献,然后就很容易想到可撤销并查集
神奇的代码
#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;
}
跟倍增复杂度差不多,甚至还快上一点
有点懒,估计没人看了吧