莫队算法详解

合集下载

匈牙利算法基本原理

匈牙利算法基本原理

匈牙利算法(Hungarian algorithm),也称为Kuhn-Munkres算法,是用于解决最大权匹配问题的一种经典算法。

它的基本原理是通过逐步改进当前匹配来寻找最优匹配。

以下是匈牙利算法的基本原理:
1. 初始化:将每个顶点的匹配状态设置为未匹配。

创建一个大小为n×n的矩阵,其中n是顶点数目。

矩阵表示图中边的权重。

2. 第一阶段:对于每个未匹配的顶点,尝试为其寻找匹配。

从该顶点开始进行深度优先搜索(DFS)或广度优先搜索(BFS)来寻找增广路径。

- 如果找到了增广路径,将路径上的顶点的匹配状态进行交替,即从未匹配转为匹配,从匹配转为未匹配。

这样可以扩展当前的匹配集。

- 如果没有找到增广路径,则转到第二阶段。

3. 第二阶段:为了找到最优匹配,需要修改图中的权重,使得原问题转化为一个完美匹配的问题。

具体步骤如下:
- 从所有未匹配的顶点中找到最小权重的边,并将该权重减去其他边的权重。

- 对于所有已匹配的顶点,将其权重加上其他边的权重。

- 重新回到第一阶段,继续寻找增广路径。

4. 重复执行第一阶段和第二阶段,直到无法找到更多的增广路径。

此时,得到了最优匹配。

匈牙利算法的时间复杂度为O(n^3),其中n是顶点数目。

它在解决二分图最大权匹配问题上表现出色,并被广泛应用于实际问题,如任务分配、资源调度等领域。

gmm算法理解

gmm算法理解

gmm算法理解
GMM算法,即高斯混合模型算法,是一种常用的聚类算法,用于将数据点划分为不同的组或类别。

它的基本思想是使用多个高斯分布来描述数据的统计特性,每个高斯分布代表一个类别。

通过估计每个高斯分布的参数,可以确定数据点属于哪个类别。

在GMM算法中,每个高斯分布由均值向量和协方差矩阵描述。

均值向量表示数据的中心位置,而协方差矩阵表示数据的形状和方向。

算法的目标是找到最优的均值向量和协方差矩阵,以最大化数据的似然性。

为了实现这个目标,GMM算法使用EM算法(期望最大化算法)进行迭代优化。

EM算法包括两个步骤:E步骤和M步骤。

在E步骤中,根据当前的参数估计,计算每个数据点属于每个类别的概率。

然后,在M步骤中,使用这些数据点的概率来更新每个类别的均值向量和协方差矩阵。

通过不断迭代这两个步骤,GMM算法可以逐渐优化参数,直到收敛。

GMM算法的优点是可以处理任意形状的数据分布,并且能够自动确定类别的数量。

它还可以通过调整高斯分布的
数量和参数来控制模型的复杂性。

然而,GMM算法也存在一些缺点,例如对初始参数的敏感性和计算复杂性较高。

在实际应用中,GMM算法常用于图像分割、语音识别、异常检测等领域。

通过合理地选择高斯分布的数量和参数,GMM算法可以有效地对数据进行聚类和分析,提取出有用的信息。

hungarian method

hungarian method

hungarian methodHungarian method是一种经典的解决分配问题的算法。

该算法在二十世纪五六十年代由匈牙利数学家Dénes Kőnig和Jenő Egerváry所发明,用于解决在线性规划中常见的任务分配问题。

这种算法结合了图论和线性规划的技术,是一种非常高效和精准的优化算法。

1. 问题定义在任务分配问题中,我们需要将n项活动分配给n个人,每个人只能完成一项活动。

每项活动有一个与之相关联的成本或权重,我们需要最小化这些权重的总和。

该问题可描述为一个n*n的矩阵,其中每个元素aij代表将任务i分配给人j所需的代价。

2. 算法步骤Hungarian method的实现步骤如下:(1)首先,对原始的代价矩阵进行列减法和行减法,得到一个新的矩阵。

(2)使用最小化(或最大化)算法,将矩阵的元素分组为行和列,并将它们连接起来。

(3)通过在每个组内选择最小的元素并在每个组之间进行替换来得到最优解。

(4)如果问题没有得到解决,则回到步骤1并继续执行算法,直到找到最优解为止。

3. 矩阵的处理在第一步中,我们需要对原始的代价矩阵进行行减法和列减法。

对于每一行和每一列,我们从其中选择一个最小的元素,并将该最小元素从行(或列)的其他元素中减去。

通过这种方式,我们可以得到一个新的矩阵,它的元素最少有一个为0。

该矩阵称为减法矩阵。

4. 匈牙利算法的实现在第二步中,我们使用最小化算法将减法矩阵的元素分组为行和列。

我们将行中的最小元素和列中的最小元素连接起来,并用直线穿过它们。

接下来,我们用相邻线覆盖矩阵的其他元素,直到矩阵的每个元素都被覆盖。

第三步是通过在组内选择最小元素并在组和列之间进行替换来获得最优解的。

如果我们无法替换元素,例如在第二步中,我们没有找到足够的相邻行或列,则需要回到第1步并继续。

5. 求解复杂度的分析Hungarian method是一种精确的分配算法,可以在多项多项任务分配问题上得到最优解。

匈牙利匹配算法的原理

匈牙利匹配算法的原理

匈牙利匹配算法的原理匈牙利匹配算法(也被称为二分图匹配算法或者Kuhn-Munkres算法)是用于解决二分图最大匹配问题的经典算法。

该算法由匈牙利数学家Dénes Kőnig于1931年提出,并由James Munkres在1957年进行改进。

该算法的时间复杂度为O(V^3),其中V是图的顶点数。

匹配问题定义:给定一个二分图G=(X,Y,E),X和Y分别代表两个不相交的顶点集合,E表示连接X和Y的边集合。

图中的匹配是指一个边的集合M,其中任意两条边没有公共的顶点。

匹配的相关概念:1.可增广路径:在一个匹配中找到一条没有被占用的边,通过这条边可以将匹配中的边个数增加一个,即将不在匹配中的边添加进去。

2. 增广路径:一个可增广路径是一个交替序列P=v0e1v1e2v2...ekvk,其中v0属于X且不在匹配中,v1v2...vk属于Y且在匹配中,e1e2...ek在原图中的边。

3.增广轨:一个交替序列形如V0E1V1E2...EkVk,其中V0属于X且不在匹配中,V1V2...Vk属于Y且在匹配中,E1E2...Ek在原图中的边。

增广轨是一条路径的特例,它是一条从X到Y的交替序列。

1.初始时,所有的边都不在匹配中。

2.在X中选择一个点v0,如果v0已经在匹配中,则找到与v0相连的在Y中的顶点v1、如果v1不在匹配中,则(v0,v1)是可增广路径的第一条边。

3. 如果v1在匹配中,则找到与v1相连的在X中的顶点v2,判断v2是否在匹配中。

依此类推,直到找到一个不在匹配中的点vn。

4.此时,如果n是奇数,则(n-1)条边在匹配中,这意味着我们找到了一条增广路径。

如果n是偶数,则(n-1)条边在匹配中,需要进行进一步的处理。

5.如果n是偶数,则将匹配中的边和非匹配中的边进行颠倒,得到一个新的匹配。

6.对于颠倒后的匹配,我们再次从第2步开始,继续寻找增广路径。

7.重复步骤2到步骤6,直到找不到可增广路径为止,此时我们得到了最大匹配。

全网最详细、最深的四类莫队算法讲解

全网最详细、最深的四类莫队算法讲解

• 我们来分析一下时间复杂度
• 这道题代码细节很重要,听懂了并不意味着会写,会写也不意味着能写对
CX数颜色
树上莫队
引入:luogu P4074 [WC2013]狗粮公园
• YQY和KXR建立了一座狗粮公园,公园里不仅有美丽的风景,还有免费狗粮 发放点。 • 狗粮公园结构奇特,由n个免费狗粮发放点构成,可以依次将其编号为1~n。 有n-1条双向道路连接着这些发放点,并且整个公园是联通的。 • YQY发放的狗粮种类丰富 ,总共有m种,依次编号为1~m,每一个狗粮 发放处都只发放某种特定的狗粮,我们用C[i]来表示第i号发放点发放的狗粮。 • 来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的发放点出 发前往另一个特定的发放点,并吃路上YQY发的狗粮。
输入格式: 输入文件第一行包含两个正整数N和M。N为袜子的数量,M为PQF所提的询问的数 量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同 的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。 输出格式: 包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出 两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。 (详见样例) 输入样例: 输出样例: 64 2/5 123332 0/1 26 1/1 13 4/15 35 16
带修莫队
• 把询问和修改分开,修改就是按照读入的顺序记录修改位置,修改前值,修 改后值;询问要记录左右端点,Tim,id(表示在此查询前做过了Tim次修改, 这是第id个查询) • 然后给询问排序(修改千万不能排),然后排序肯定也要改,我们把L所在 块做第一关键字,R所在块做第二关键字,Tim做第三关键字。 • 排完序在解决询问时,比普通莫队多需要一个指针Time,表示已经完成了多 少个修改。然后在普通莫队前面判读是否要时光推移或时光倒流即可

莫队算法的应用

莫队算法的应用

莫队算法的应用
莫队算法是一种离线查询算法,主要应用于解决一类不带修改的区间查询问题。

其基本思想是将询问按照某种顺序排序,然后按照排序后的顺序依次处理询问,从而得到每个询问的答案。

在处理每个询问时,莫队算法利用相邻两个询问之间的信息,通过局部调整的方式,将前一个询问的解答转化为后一个询问的解答,避免了重复计算和全局扫描,从而提高了算法的效率。

莫队算法的应用非常广泛,可以解决很多实际问题。

比如给定一个长度为N的数组,数组中所有元素的大小不超过N,需要回答M个查询,每个查询的形式是L和R,要求计算在范围[L, R]中至少出现3次的数字的个数。

这个问题可以使用莫队算法来解决。

具体来说,我们可以将数组分块,然后按照左端点所在块为第一关键字、右端点为第二关键字对询问进行排序。

然后依次处理排序后的询问,利用相邻两个询问之间的信息,通过增加或删除元素的方式,动态维护当前区间内至少出现3次的数字的个数。

这样就可以在O(N√N)的时间复杂度内解决该问题。

除了上述应用外,莫队算法还可以应用于其他领域,如字符串处理、图论等。

在字符串处理中,可以利用莫队算法解决一些涉及子串的问题,如最长回文子串、最长重复子串等。

在图论中,可以利用莫队算法解决一些涉及图上路径的问题,如路径长度、路径上的最大值等。

需要注意的是,在使用莫队算法时,需要根据具体问题的特点选择合适的排序方式和维护方式,以达到最优的算法效率。

总之,莫队算法是一种非常实用的离线查询算法,可以应用于各种领域解决不同的问题。

掌握莫队算法的基本思想和应用技巧对于提高算法设计和解决问题的能力非常重要。

支持向量机原理(四)SMO算法原理

支持向量机原理(四)SMO算法原理

⽀持向量机原理(四)SMO算法原理 在SVM的前三篇⾥,我们优化的⽬标函数最终都是⼀个关于\alpha向量的函数。

⽽怎么极⼩化这个函数,求出对应的\alpha向量,进⽽求出分离超平⾯我们没有讲。

本篇就对优化这个关于\alpha向量的函数的SMO算法做⼀个总结。

1. 回顾SVM优化⽬标函数 我们⾸先回顾下我们的优化⽬标函数: \underbrace{ min }_{\alpha} \frac{1}{2}\sum\limits_{i=1,j=1}^{m}\alpha_i\alpha_jy_iy_jK(x_i,x_j) - \sum\limits_{i=1}^{m}\alpha_i s.t. \; \sum\limits_{i=1}^{m}\alpha_iy_i = 0 0 \leq \alpha_i \leq C 我们的解要满⾜的KKT条件的对偶互补条件为:\alpha_{i}^{*}(y_i(w^Tx_i + b) - 1 + \xi_i^{*}) = 0 根据这个KKT条件的对偶互补条件,我们有:\alpha_{i}^{*} = 0 \Rightarrow y_i(w^{*} \bullet \phi(x_i) + b) \geq 1 0 <\alpha_{i}^{*} < C \Rightarrow y_i(w^{*} \bullet \phi(x_i) + b) = 1 \alpha_{i}^{*}= C \Rightarrow y_i(w^{*} \bullet \phi(x_i) + b) \leq 1 由于w^{*} = \sum\limits_{j=1}^{m}\alpha_j^{*}y_j\phi(x_j),我们令g(x) = w^{*} \bullet \phi(x) + b=\sum\limits_{j=1}^{m}\alpha_j^{*}y_jK(x, x_j)+ b^{*},则有:\alpha_{i}^{*} = 0 \Rightarrow y_ig(x_i) \geq 1 0 < \alpha_{i}^{*} < C\Rightarrow y_ig(x_i) = 1 \alpha_{i}^{*}= C \Rightarrow y_ig(x_i) \leq 12. SMO算法的基本思想 上⾯这个优化式⼦⽐较复杂,⾥⾯有m个变量组成的向量\alpha需要在⽬标函数极⼩化的时候求出。

(莫队算法)两题莫队算法统计数量的入门题

(莫队算法)两题莫队算法统计数量的入门题

(莫队算法)两题莫队算法统计数量的⼊门题因为这两题差不多,⽽且⽐较简单,就放⼀起,做了这题,这种题⽬就是巨⽔的题了。

随便写都⾏。

Powerful array题意:多次查询数列中从L到R每个数字出现次数的平⽅乘这个数字的和。

代码:1 #include <cstdio>2 #include <cstring>3 #include <iostream>4 #include <cmath>5 #include <map>6 #include <vector>7 #include <algorithm>89using namespace std;1011const int maxn = 200010;12const int bk = 350;13int n, q;14int a[maxn];15struct Query {16int l, r, index;17 };18int cnt[maxn * 5];1920 Query query[maxn];2122bool cmp(Query a, Query b) {23if(a.l / bk == b.l / bk)return a.r < b.r;24else return a.l / bk < b.l / bk;25 }2627long long ans[maxn];28long long res = 0;2930void add(int x) {31 res += x * (2 * cnt[x] + 1);32 cnt[x]++;33 }3435void del(int x) {36 cnt[x]--;37 res -= x * (2 * cnt[x] + 1);38 }3940int main() {41 scanf("%d", &n);42 scanf("%d", &q);43for(int i = 1; i <= n; i++) {44 scanf("%d", &a[i]);45 }46for(int i = 0; i < q; i++) {47 scanf("%d%d", &query[i].l, &query[i].r);48 query[i].index = i;49 }50 sort(query, query + q, cmp);51int left = 1, right = 0;52for(int i = 0; i < q; i++) {53if(right < query[i].r) {54for(int j = right + 1; j <= query[i].r; j++) {55 add(a[j]);56 }57 } else {58for(int j = right; j > query[i].r; j--) {59 del(a[j]);60 }61 }62 right = query[i].r;63if(left < query[i].l) {64for(int j = left; j < query[i].l; j++) {65 del(a[j]);66 }6768 } else {69for(int j = left - 1; j >= query[i].l; j--) {70 add(a[j]);71 }72 }73 left = query[i].l;74 ans[query[i].index] = res;75 }76for(int i = 0; i < q; i++) {77 printf("%lld\n", ans[i]);7879 }8081return0;82 }View CodeSona题意:多次查询数列中L到R买个数字出现次数的⽴⽅和。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

莫队算法详解本文翻译自MO’s Algorithm (Query square root decomposition),作者anudeep2011,发表日期为2014-12-28。

由于最近碰到一些莫队算法的题目,找到的相关中文资料都比较简略,而这篇英语文章则讲解的比较详细,故翻译成中文与大家分享。

由于本人水平有限,错误在所难免,请谅解。

下面是译文。

我又发现了一个有用,有趣但网上资源非常少的话题。

在写作之前,我做了一个小调查,令我惊讶的是,几乎所有的印度程序员都不知道该算法。

学习这个很重要,事实上所有的codeforces 红名程序员都使用这个算法,比如在div 1 C 题和D 题中。

在一年半以前没有这方面的题目,但从那时起这类题目的数量就爆发了!我们可以期待这在未来的比赛中会有更多的这类题目。

莫队算法详解问题描述复杂度的简单的解法一个解决上述问题的算法及其正确性对上述算法的复杂性证明 - 上述算法的适用范围习题和示例代码问题描述给定一个大小为N 的数组,数组中所有元素的大小<=N 。

你需要回答M 个查询。

每个查询的形式是L ,R 。

你需要回答在范围[ L ,R ]中至少重复3次的数字的个数。

例如:数组为{ 1,2,3,1,1,2,1,2,3,1 }(索引从0开始)查询:L = 0,R = 4。

答案= 1。

在范围[L ,R]中的值 = { 1,2,3,1,1 },只有1是至少重复3次的。

查询:L = 1, R = 8。

答案= 2。

在范围[L ,R]中的值 = { 2,3,1,1,2,1,2,3 }, 1重复3遍并且2重复3次。

至少重复3次的元素数目=答案= 2。

复杂度的简单的解法对于每一个查询,从L 至R 循环,统计元素出现频率,报告答案。

考虑M = N 的情况,以下程序在最O ()N 2O (∗N )N −−√O ()N 22坏的情况运行在for each query:answer = 0count[] = 0for i in {l..r}:count[array [i]]++if count[array [i]] == 3:answer++对上述算法稍作修改。

它仍然运行在add (position):count[array [position]]++if count[array [position]] == 3:answer++remove (position):count[array [position]]--if count[array [position]] == 2:answer--currentL = 0currentR = 0answer = 0count[] = 0for each query:// currentL 应当到 L, currentR 应当到 Rwhile currentL < L:remove (currentL)currentL++while currentL > L:add (currentL)currentL--while currentR < R:add (currentR)currentR++while currentR > R:remove (currentR)currentR--output answer最初我们总是从L 至R 循环,但现在我们从上一次查询的位置调整到当前的查询的位置。

如果上一次的查询是L = 3,R = 10,则我们在查询结束时有currentL=3、currentR=10。

如果下一个查询是L = 5,R = 7,则我们将currentL 移动到5,currentR 移动到7。

add 函数 意味着我们添加该位置的元素到当前集合内,并且更新相应的回答。

O ()n 2O ()n 2remove 函数 意味着我们从当前集合内移除该位置的元素,并且更新相应的回答。

一个解决上述问题的算法及其正确性莫队算法仅仅调整我们处理查询的顺序。

我们得到了M 个查询,我们将把查询以一个特定的顺序进行重新排序,然后处理它们。

显然,这是一个离线算法。

每个查询都有L 和R ,我们称呼其为“起点”和“终点”。

让我们将给定的输入数组分为块。

每一块的大小为 。

每个“起点”落入其中的一块。

每个“终点”也落入其中的一块。

如果某查询的“起点”落在第p 块中,则该查询属于第p 块。

该算法将处理第1块中的查询,然后处理第2块中的查询,等等,最后直到第块。

我们已经有一个顺序、查询按照所在的块升序排列。

可以有很多的查询属于同一块。

从现在开始,我会忽略(其它,译者注,所有括号内斜体同)所有的块,只关注我们如何询问和回答第1块。

我们将对所有块做同样的事。

(第1块中的)所有查询的“起点”属于第1块,但“终点”可以在包括第1块在内的任何块中。

现在让我们按照R 值升序的顺序重新排列这些查询。

我们也在所有的块中做这个操作。

(指每个块块内按R 升序排列。

)最终的排序是怎样的?所有的询问首先按照所在块的编号升序排列(所在块的编号是指询问的“起点”属于的块)。

如果编号相同,则按R 值升序排列。

例如考虑如下的询问,假设我们会有3个大小为3的块(0-2,3-5,6-8):{0, 3} {1, 7} {2, 8} {7, 8} {4, 8} {4, 4} {1, 2}让我们先根据所在块的编号重新排列它们{0, 3} {1, 7} {2, 8} {1, 2} (|){4, 8} {4, 4}(|) {7, 8}现在我们按照R 的值重新排列{1, 2} {0, 3} {1, 7} {2, 8}(|) {4, 4} {4, 8}(|) {7, 8}现在我们使用与上一节所述相同的代码来解决这个问题。

上述算法是正确,因为我们没有做任何改变,只是重新排列了查询的顺序。

对上述算法的复杂性证明 - 我们完成了莫队算法,它只是一个重新排序。

可怕的是它的运行时分析。

原来,如果我们按照我上面指定的顺序,我们所写的的代码运行在时间复杂度上。

可怕,这是正确的,仅仅是重新排序查询使我们把复杂度从降低到,而且也没有任何进一步的代码上的修改。

好哇!我们将以的复杂度AC 。

看看我们上面的代码,所有查询的复杂性是由 4个while 循环决定的。

前2个while 循环可以表述为“左指针(currentL)的移动总量”,后2个 while 循环可以表述为“右指针(currentR)的移动总量”。

这两者的和将是总复杂性。

N −−√N /=N −−√N −−√N −−√O (∗N )N −−√O ()N 2O (×N )N −−√O ()N 2O (×N )N −−√O (×N )N −−√最重要的。

让我们先谈论右指针。

对于每个块,查询是递增的顺序排序,所以右指针(currentR )按照递增的顺序移动。

在下一个块的开始时,指针可能在extreme end (最右端?) ,将移动到下一个块中的最小的R 处。

这意味着对于一个给定的块,右指针移动的量是。

我们有块,所以总共是。

太好了!让我们看看左指针怎样移动。

对于每个块,所有查询的左指针落在同一个块中,当我们从一个查询移动到另个一查询左指针会移动,但由于前一个L 与当前的L 在同一块中,此移动是(块大小)的。

在每一块中左指针的移动总量是,Q 是落在那个块的查询的数量。

对于所有的块,总的复杂度为。

就是这样,总复杂度为上述算法的适用范围如前所述,该算法是离线的,这意味着当我们被强制按照特定的顺序查询时,我们不能再使用它。

这也意味着当有更新操作时我们不能用这个算法。

不仅如此,一个重要的可能的局限性:我们应该能够编写add 和remove 函数。

会有很多的情况下,add 是平凡的 (指复杂度?),但remove 不是。

这样的一个例子就是我们想要求区间内最大值。

当我们添加的元素,我们可以跟踪最大值。

但当我们删除元素则不是平凡的。

不管怎样,在这种情况下,我们可以使用一个集合来添加元素,删除元素和报告 最小值(作者想说最大值?)。

在这种情况下,添加和删除操作都是(导致了的算法)。

在许多情况下,我们可以使用此算法。

在一些情况下,我们也可以使用其它的数据结构,如线段树,但对于一些问题使用莫队算法的是必须的。

让我们在下一节中讨论几个问题。

习题和示例代码DQUERY – SPOJ :区间内不同元素的数量=出现次数>=1的元素个数,所以跟以上讨论的问题是一样的。

点击此处查看示例代码注意:该代码提交会超时,加上更快的输入输出(传说中的输入输出挂?) 就能AC 。

为了使代码整洁,去掉了快的输入输出。

(评论中说将remove 和add 声明为inline 内联函数就可以AC 。

)Powerful array – CF Div1 D : 这道题是必须用莫队算法的例子。

我找不到任何其它解法。

CF Div1 D 意味着这是一道难题。

看看用了莫队算法就多简单了。

你只需要修改上述代码中的add(), remove() 函数。

GERALD07 – CodechefGERALD3 – CodechefTree and Queries – CF Div1 DPowerful Array – CF Div1 DJeff and Removing Periods – CF Div1 DO (N )O ()N −−√O (N ∗)N −−√O ()N −−√O (Q ∗)N −−√O (M ∗)N −−√O ((N +M )∗)=O (N ∗)N −−√N −−√O (1)O (logN )O (N ∗∗logN )N −−√Sherlock and Inversions – Codechef (省略结束语)。

相关文档
最新文档