问题
给出平面上若干个点,两点之间的距离为两点之间的曼哈顿距离,求这些点的最小生成树
例题传送门
两点的曼哈顿距离:
定理
- 对于图上任意一个环,环上边权最大的边一定不在最小生成树上
证明:- 环上至少有一条边不在最小生成树上,若环上所有边都在最小生成树上,那不满足树的性质
- 在树上任意断开一条边,最小生成树必然分成两棵子树
- 假设环上边权最大的边在最小生成树上,在最小生成树上断开这条最大的边,新产生的两棵子树显然可以通过连接原先环上的边重新形成一棵树,而这条边的权值显然小于已经断开的边,所以总权值变小了,与原先的树是最小生成树相矛盾
做法
分析
- 根据上述定理,显然图中的蓝色边是不需要连接的,它不可能出现在最小生成树的答案中
- 对于每个点,以它作为原点,以图示区域为例,因为所有边都是无向边,所以只需要连接这个区域内离它最近的点,也就是只需要连接绿色边
以图示区域为例
设原点上的点为,则对于其余点
显然有
于是有
显然对于满足条件的,
所以我们只需要对每个点按照离散后,对点按照
降序排序在线段树或者树状数组上维护
的最小值,查询后插入当前点即可
完善
对于一个二维平面,显然可以划分为这样个区域
对于
轴右侧的区域,可以通过交换点枚举顺序的方法处理
- 区域
就是上述的例子
- 区域
的点可以通过关于直线
对称变换到区域
中
- 区域
的点可以通过在区域
的变换的基础上关于
轴对称得到
- 区域
的点可以通过在区域
的变换的基础上,关于直线
对称得到
反向树状数组
对于维护大于某个数的区间,可以通过反向树状数组来做
传统的树状数组,每个点维护的区间范围是,而反向树状数组每个点维护的区间范围是
- 查询操作,每次
- 更新操作,每次
代码
贴一段例题的ac代码,中间包含模板
// #pragma GCC optimize(3,"Ofast","inline")
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <vector>
#include <string>
#include <iostream>
#include <list>
#include <cstdlib>
#include <bitset>
#include <assert.h>
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
// char buf[(1 << 21) + 1], * p1 = buf, * p2 = buf;
// #define int long long
#define lowbit(x) (x & (-x))
#define lson root << 1, l, mid
#define rson root << 1 | 1, mid + 1, r
#define pb push_back
typedef unsigned long long ull;
typedef long long ll;
typedef std::pair<int, int> pii;
typedef std::pair<ll, ll> pll;
#define bug puts("BUG")
const long long INF = 0x3f3f3f3f3f3f3f3fLL;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-6;
template <class T>
inline void read(T &x)
{
int sign = 1;char c = getchar();x = 0;
while (c > '9' || c < '0'){if (c == '-')sign = -1;c = getchar();}
while (c >= '0' && c <= '9'){x = x * 10 + c - '0';c = getchar();}
x *= sign;
}
#ifdef LOCAL
FILE* _INPUT=freopen("input.txt", "r", stdin);
// FILE* _OUTPUT=freopen("output.txt", "w", stdout);
#endif
using namespace std;
const int maxn = 1e5 + 10;
struct EDGE
{
int u, v, w;
bool operator < (const EDGE &o)const
{
return w < o.w;
}
} edge[maxn << 3];
int f[maxn], tot;
int find(int x)
{
return f[x] == x ? x : f[x] = find(f[x]);
}
void joint(int u, int v)
{
int fu = find(u), fv = find(v);
if (fu != fv)
f[fu] = fv;
}
ll kurskal(int n)
{
ll res = 0;
for (int i = 0; i <= n; ++i)
f[i] = i;
sort(edge, edge + tot);
for (int i = 0; i < tot; ++i)
{
if (find(edge[i].u) != find(edge[i].v))
{
joint(edge[i].u, edge[i].v);
res += edge[i].w;
}
}
return res;
}
struct POINT
{
int x, y, id;
} point[maxn], temp[maxn];
struct BIT
{
int _N;
pii bit[maxn];
void init(int n)
{
_N = n;
for (int i = 1; i <= n; ++i)
{
bit[i] = pii(inf, -1);
}
}
void update(int k,pii v)
{
for (; k > 0; k -= lowbit(k))
{
bit[k] = min(bit[k], v);
}
}
pii query (int k)
{
pii res(inf, -1);
for (; k <= _N; k += lowbit(k))
{
res = min(res, bit[k]);
}
return res;
}
} bit;
int a[maxn], b[maxn];
void manhattan_mst(POINT p[], int n)
{
for (int cas = 0; cas < 4; ++cas)
{
if (cas == 1 || cas == 3)
for (int i = 0; i < n; ++i)
swap(p[i].x, p[i].y);
else if (cas == 2)
for (int i = 0; i < n; ++i)
p[i].x = -p[i].x;
for (int i = 0; i < n; ++i)
{
b[i] = p[i].y - p[i].x;
}
sort(b, b + n);
int m = unique(b, b + n) - b;
sort(p, p + n, [](POINT a, POINT b) {
return a.x > b.x;
});
bit.init(m);
for (int i = 0; i < n; ++i)
{
int l = lower_bound(b, b + m, p[i].y - p[i].x) - b;
pii res = bit.query(l + 1);
if (res.second != -1)
edge[tot++] = {p[i].id, res.second, res.first - p[i].x - p[i].y};
bit.update(l + 1, pii(p[i].x + p[i].y, p[i].id));
}
}
}
int main()
{
int n, m;
while (~scanf("%d%d", &n, &m))
{
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
point[(i - 1) * m + j].id = (i - 1) * m + j;
read(point[(i - 1) * m + j].x);
}
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
read(point[(i - 1) * m + j].y);
}
}
tot = 0;
for (int i = 1; i <= n; ++i)
{
int len = 0;
for (int j = 1; j <= m; ++j)
{
temp[len++] = point[(i - 1) * m + j];
}
manhattan_mst(temp, len);
}
for (int j = 1; j <= m; ++j)
{
int len = 0;
for (int i = 1; i <= n; ++i)
{
temp[len++] = point[(i - 1) * m + j];
}
manhattan_mst(temp, len);
}
printf("%lld\n", kurskal(n * m));
}
}


京公网安备 11010502036488号