一、曼哈顿距离最小生成树

曼哈顿距离最小生成树问题可以简述如下:

给定二维平面上的N个点,在两点之间连边的代价为其曼哈顿距离,求使所有点连通的最小代价。

朴素的算法可以用O(N2)的Prim,或者处理出所有边做Kruskal,但在这里总边数有O(N2)条,所以Kruskal的复杂度变成了O(N2logN)。

但是事实上,真正有用的边远没有O(N2)条。我们考虑每个点会和其他一些什么样的点连边。可以得出这样一个结论,以一个点为原点建立直角坐标系,在每45度内只会向距离该点最近的一个点连边。

这个结论可以证明如下:假设我们以点A为原点建系,考虑在y轴向右45度区域内的任意两点B(x1,y1)和C(x2,y2),不妨设|AB|≤|AC|(这里的距离为曼哈顿距离),如下图:

|AB|=x1+y1,|AC|=x2+y2,|BC|=|x1-x2|+|y1-y2|。而由于B和C都在y轴向右45度的区域内,有y-x>0且x>0。下面我们分情况讨论:

1.      x1>x2且y1>y2。这与|AB|≤|AC|矛盾;

2.      x1≤x2且y1>y2。此时|BC|=x2-x1+y1-y2,|AC|-|BC|=x2+y2-x2+x1-y1+y2=x1-y1+2*y2。由前面各种关系可得y1>y2>x2>x1。假设|AC|<|BC|即y1>2*y2+x1,那么|AB|=x1+y1>2*x1+2*y2,|AC|=x2+y2<2*y2<|AB|与前提矛盾,故|AC|≥|BC|;

3.      x1>x2且y1≤y2。与2同理;

4.      x1≤x2且y1≤y2。此时显然有|AB|+|BC|=|AC|,即有|AC|>|BC|。

综上有|AC|≥|BC|,也即在这个区域内只需选择距离A最近的点向A连边。

这种连边方式可以保证边数是O(N)的,那么如果能高效处理出这些边,就可以用Kruskal在O(NlogN)的时间内解决问题。下面我们就考虑怎样高效处理边。

我们只需考虑在一块区域内的点,其他区域内的点可以通过坐标变换“移动”到这个区域内。为了方便处理,我们考虑在y轴向右45度的区域。在某个点A(x0,y0)的这个区域内的点B(x1,y1)满足x1≥x0且y1-x1>y0-x0。这里对于边界我们只取一边,但是操作中两边都取也无所谓。那么|AB|=y1-y0+x1-x0=(x1+y1)-(x0+y0)。在A的区域内距离A最近的点也即满足条件的点中x+y最小的点。因此我们可以将所有点按x坐标排序,再按y-x离散,用线段树或者树状数组维护大于当前点的y-x的最小的x+y对应的点。时间复杂度O(NlogN)。

至于坐标变换,一个比较好处理的方法是第一次直接做;第二次沿直线y=x翻转,即交换x和y坐标;第三次沿直线x=0翻转,即将x坐标取相反数;第四次再沿直线y=x翻转。注意只需要做4次,因为边是双向的。

至此,整个问题就可以在O(NlogN)的复杂度内解决了。

二、莫队算法

据说这个算法是莫涛提出的(Orz!),但是在网上到处都搜不到相关资料,最后问pty才知道的。这个算法是用于处理一类不带修改的区间查询问题的离线算法,其核心在于利用曼哈顿距离最小生成树算法对区间处理顺序进行处理。比如下面这个例题(清橙A1206《小Z的袜子》,就是莫队出的题):

给定一个长为N的序列,每个元素的值是其颜色。有M次询问,每次询问从一个区间中随机选取两个元素同色的概率。

一次询问[l,r]的答案即,其中是区间中第i中颜色的个数。显然暴力是O(NM)的,而且一般的区间问题的思路似乎不适用。

我们先考虑一个简化的问题:所有的查询区间的左端点都是1。那么我们可以按右端点排序,假设已经处理出了[1,r]的答案,考虑转移到[1,r+k],即添加k个元素,这个可以在O(k)的复杂度内求出。那么处理所有区间的复杂度(不考虑排序)就是O(N)。

那么如果是从[l,r]转移到[l’,r’]呢?复杂度即O(|r’-r|+|l’-l|),也即点(l,r)到点(l’,r’)的曼哈顿距离。那么如果将所有询问转化成二维平面中的点,求曼哈顿距离最小生成树,再按照生成树的顺序做,就可以最小化区间之间转移的复杂度。可以证明(我不会证……似乎莫队的论文里有),这样做的复杂度是O(N1.5)的。问题也就得到了解决。

代码:

POJ3241 Object Clustering 求曼哈顿距离最小生成树上第k大的边

//POJ3241; Object Clustering; Manhattan Distance MST
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define N 100000
#define INFI 123456789struct point
{int x, y, n;bool operator < (const point &p) const{ return x == p.x ? y < p.y : x < p.x; }
}p[N + 1];
struct inedge
{int a, b, w;bool operator < (const inedge &x) const{ return w < x.w; }
}e[N << 3 | 1];
struct BITnode
{int w, p;
}arr[N + 1];
int n, k, tot = 0, f[N + 1], a[N + 1], *l[N + 1], ans;template <typename T>
inline T abs(T x)
{ return x < (T)0 ? -x : x; }int find(int x)
{ return x == f[x] ? x : f[x] = find(f[x]); }inline bool cmp(int *a, int *b)
{ return *a < *b; }inline int query(int x)
{int r = INFI, p = -1;for (; x <= n; x += x & -x)if (arr[x].w < r) r = arr[x].w, p = arr[x].p;return p;
}inline void modify(int x, int w, int p)
{for (; x > 0; x -= x & -x)if (arr[x].w > w) arr[x].w = w, arr[x].p = p;
}inline void addedge(int a, int b, int w)
{++tot;e[tot].a = a, e[tot].b = b, e[tot].w = w;
//  printf("%d %d %d\n", a, b, w);
}inline int dist(point &a, point &b)
{ return abs(a.x - b.x) + abs(a.y - b.y); }int main()
{//Initializescanf("%d%d", &n, &k);for (int i = 1; i <= n; ++i){scanf("%d%d", &p[i].x, &p[i].y);p[i].n = i;}//Solvefor (int dir = 1; dir <= 4; ++dir){//Coordinate transform - reflect by y=x and reflect by x=0if (dir == 2 || dir == 4)for (int i = 1; i <= n; ++i) p[i].x ^= p[i].y ^= p[i].x ^= p[i].y;else if (dir == 3)for (int i = 1; i <= n; ++i) p[i].x = -p[i].x;//Sort points according to x-coordinatestd::sort(p + 1, p + n + 1);//Discretizefor (int i = 1; i <= n; ++i) a[i] = p[i].y - p[i].x, l[i] = &a[i];std::sort(l + 1, l + n + 1, cmp);/*int cnt = 1;for (int i = 2; i <= n; ++i)if (*l[i] != *l[i - 1]) *l[i - 1] = cnt++;else *l[i - 1] = cnt;*l[n] = cnt;*/for (int i = 1; i <= n; ++i) *l[i] = i;//Initialize BITfor (int i = 1; i <= n; ++i) arr[i].w = INFI, arr[i].p = -1;//Find points and add edgesfor (int i = n; i > 0; --i){int pos = query(a[i]);if (pos != -1)addedge(p[i].n, p[pos].n, dist(p[i], p[pos]));modify(a[i], p[i].x + p[i].y, i);}}//Kruskalstd::sort(e + 1, e + tot + 1);for (int i = 1; i <= n; ++i) f[i] = i;for (int i = 1, ec = n; ec > k && i <= tot; ++i)if (find(e[i].a) != find(e[i].b)){f[find(e[i].a)] = find(e[i].b);if (--ec == k) ans = e[i].w;}printf("%d\n", ans);return 0;
}

Tsinsen A1206 小Z的袜子(其实这题暴力有60分……)

//Tsinsen A1206; 小Z的袜子; Modui Algorithm
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define N 50000
#define Q 50000
#define INFI 123456789typedef long long ll;
struct edge
{int next, node;
}e[Q << 1 | 1];
int head[N + 1], tot = 0;
struct point
{int x, y, n;bool operator < (const point &p) const{ return x == p.x ? y < p.y : x < p.x; }
}qt[Q + 1], p[Q + 1];
struct inedge
{int a, b, w;bool operator < (const inedge &x) const{ return w < x.w; }
}ie[Q << 3 | 1];
int cnt = 0;
struct BITnode
{int w, p;
}arr[Q + 1];
int n, q, col[N + 1], a[Q + 1], *l[Q + 1], f[N + 1], c[N + 1];
ll cur, ans[Q + 1];
bool v[Q + 1];template <typename T>
inline T abs(T x)
{ return x < (T)0 ? -x : x; }inline int dist(const point &a, const point &b)
{ return abs(a.x - b.x) + abs(a.y - b.y); }inline void addinedge(int a, int b, int w)
{++cnt;ie[cnt].a = a, ie[cnt].b = b, ie[cnt].w = w;
}inline void addedge(int a, int b)
{e[++tot].next = head[a];head[a] = tot, e[tot].node = b;
}inline bool cmp(int *a, int *b)
{ return *a < *b; }inline int query(int x)
{int r = INFI, p = -1;for (; x <= q; x += x & -x)if (arr[x].w < r) r = arr[x].w, p = arr[x].p;return p;
}inline void modify(int x, int w, int p)
{for (; x > 0; x -= x & -x)if (arr[x].w > w) arr[x].w = w, arr[x].p = p;
}int find(int x)
{ return x == f[x] ? x : f[x] = find(f[x]); }inline ll calc(int x)
{ return (ll)x * (x - 1); }inline void add(int l, int r)
{for (int i = l; i <= r; ++i){cur -= calc(c[col[i]]);cur += calc(++c[col[i]]);}
}inline void remove(int l, int r)
{for (int i = l; i <= r; ++i){cur -= calc(c[col[i]]);cur += calc(--c[col[i]]);}
}void dfs(int x, int l, int r)
{v[x] = true;//Process right boundif (r < qt[x].y)add(r + 1, qt[x].y);else if (r > qt[x].y)remove(qt[x].y + 1, r);//Process left boundif (l < qt[x].x)remove(l, qt[x].x - 1);else if (l > qt[x].x)add(qt[x].x, l - 1);ans[x] = cur;//Moving on to next queryfor (int i = head[x]; i; i = e[i].next){if (v[e[i].node]) continue;dfs(e[i].node, qt[x].x, qt[x].y);}//Revert changes//Process right boundif (r < qt[x].y)remove(r + 1, qt[x].y);else if (r > qt[x].y)add(qt[x].y + 1, r);//Process left boundif (l < qt[x].x)add(l, qt[x].x - 1);else if (l > qt[x].x)remove(qt[x].x, l - 1);
}ll gcd(ll a, ll b)
{ return !b ? a : gcd(b, a % b); }int main()
{//Initializescanf("%d%d", &n, &q);for (int i = 1; i <= n; ++i) scanf("%d", col + i);for (int i = 1; i <= q; ++i) scanf("%d%d", &qt[i].x, &qt[i].y);//Manhattan MSTfor (int i = 1; i <= q; ++i) p[i] = qt[i], p[i].n = i;for (int dir = 1; dir <= 4; ++dir){//Coordinate transformif (dir == 2 || dir == 4)for (int i = 1; i <= q; ++i) std::swap(p[i].x, p[i].y);else if (dir == 3)for (int i = 1; i <= q; ++i) p[i].x = -p[i].x;//Sort by x-coordinatestd::sort(p + 1, p + q + 1);//Discretizefor (int i = 1; i <= q; ++i) a[i] = p[i].y - p[i].x, l[i] = &a[i];std::sort(l + 1, l + q + 1, cmp);int cnt = 1;for (int i = 2; i <= q; ++i)if (*l[i] == *l[i - 1]) *l[i - 1] = cnt;else *l[i - 1] = cnt++;*l[q] = cnt;//Initialize BITfor (int i = 1; i <= q; ++i) arr[i].w = INFI, arr[i].p = -1;//Find points and add edgesfor (int i = q; i > 0; --i){int pos = query(a[i]);if (pos != -1)addinedge(p[i].n, p[pos].n, dist(p[i], p[pos]));modify(a[i], p[i].x + p[i].y, i);}}//Kruskalstd::sort(ie + 1, ie + cnt + 1);//Initialize disjoint setfor (int i = 1; i <= q; ++i) f[i] = i;//Add edgesfor (int i = 1; i <= cnt; ++i)if (find(ie[i].a) != find(ie[i].b)){f[find(ie[i].a)] = find(ie[i].b);addedge(ie[i].a, ie[i].b), addedge(ie[i].b, ie[i].a);}//Modui Algorithm++c[col[1]];dfs(1, 1, 1);//Outputfor (int i = 1; i <= q; ++i){ll x = ans[i], y = calc(qt[i].y - qt[i].x + 1);if (!x) printf("0/1\n");else{ll g = gcd(x, y);printf("%I64d/%I64d\n", x / g, y / g);}}return 0;
}

曼哈顿距离最小生成树与莫队算法相关推荐

  1. 曼哈顿距离最小生成树与莫队算法(总结)

    曼哈顿距离最小生成树与莫队算法(总结) 1 曼哈顿距离最小生成树 曼哈顿距离最小生成树问题可以简述如下:  给定二维平面上的N个点,在两点之间连边的代价为其曼哈顿距离,求使所有点连通的最小代价.  朴 ...

  2. 曼哈顿距离最小生成树

    一.参考博客 博客:曼哈顿距离最小生成树与莫队算法 博客:学习总结:最小曼哈顿距离生成树 二.前置知识 1.曼哈顿距离:给定二维平面上的N个点,在两点之间连边的代价.(即distance(P1,P2) ...

  3. 曼哈顿距离最小生成树莫队算法

    参考资料:https://www.cnblogs.com/CsOH/p/5904430.html https://blog.csdn.net/huzecong/article/details/8576 ...

  4. BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题学习笔记】

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MB Submit: 9894  Solved: 4561 [Su ...

  5. 【BZOJ2038】小Z的袜子,第一次的莫队算法

    传送门 写在前面:莫队竟如此暴力-- 思路:当初我对这个题的第一感觉--这个区间问题可以用线段树或者树状数组?答案当然是不能,于是我就去简单学了下莫队算法.在我看来,莫队(分块版,不是二维曼哈顿距离什 ...

  6. BZOJ2038 小Z的袜子 (莫队算法)

    题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=2038 专题练习: http://acm.hust.edu.cn/vjudge/conte ...

  7. 【学习笔记】莫队算法

    莫队算法 确实是看过的最良心的讲解: https://www.cnblogs.com/CsOH/p/5904430.html 问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答 ...

  8. hdu 5145 NPY and girls (莫队算法)

    题意:有一个长度为n的数字序列,m次询问一个区间l-r中数字重新排列的方案数(mod 10^9+7). 明显的莫队算法,只需要排序,然后预处理一下逆元就可以了. 所谓的莫队算法,最初版本是求曼哈顿距离 ...

  9. hdu 5381 2015多校第八场 莫队算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5381 还没学过莫队算法....网上也找不到莫队算法的论文,只能勉强看着别人的代码打下来... 稍微介绍 ...

最新文章

  1. 蓝懿教育九月二十七日记录
  2. EID-:宏病毒组技术在新发腹泻病毒鉴定中的应用
  3. php某列为键数组为值,PHP 将二维数组中某列值作为数组的键名 -- 超实用
  4. git如何忽略已经提交的文件 (.gitignore文件无效)
  5. Ext2.2系列(50篇)
  6. 1997年投稿,2021年发表!收到录用信那一刻,我即将退休……
  7. spark基础之spark streaming的checkpoint机制
  8. 基于Session的认证方式_实现授权功能_Spring Security OAuth2.0认证授权---springcloud工作笔记118
  9. leetcode isPalindrome (回文数判断)
  10. uboot常用命令详解 2
  11. SL4A/Py4A直接在adb中用python加载脚本
  12. 200与mcgs485实例 smart_SMAART200与MCGS-工业支持中心-西门子中国
  13. Mcafee官方卸载工具
  14. 《认知与设计——理解UI设计准则》系列笔记目录
  15. Oracle_登录数据库系统
  16. QNAP-NAS外网访问——aliyun-ddns,docker,myqnapcloud
  17. android SENSOR_ACCELEROMETER 三轴加速度传感器
  18. windows10 android模拟器,手机windows10模拟器安卓版
  19. 【传智播客郑州校区】辞掉7年工作转行程序员,为了理想在传智播客前行
  20. PC使用--记录电脑硬件使用问题

热门文章

  1. jQuery中“$”的用法
  2. iOS 高德地图怎么在屏幕内显示所有的Marker?
  3. 【面经】高德地图 C++ 研发 一面
  4. React Native不同设备分辨率适配和设计稿尺寸单位px的适配
  5. UGP是什么?UGP VR眼镜怎么样,UGP VR好吗,一分钟了解UGP
  6. android 融球效果图,加载动效制作(普及一些小常识)+刷人气^_^_html/css_WEB-ITnose...
  7. Java集合 学习记录
  8. OpenResty 快速入门
  9. 关于oracle的判断题,oracle笔试题
  10. linux系统用什么手柄,如何直接在Linux系统中处理来自角色设备/游戏手柄的输入?...