分治算法

POJ 1741 Tree (点分治)

為{幸葍}努か 提交于 2020-04-01 05:15:15
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 20816 Accepted: 6820 Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k. Write a program that will count how many pairs which are valid for a given tree. Input The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l

学习笔记: cdq分治

你说的曾经没有我的故事 提交于 2020-03-25 03:10:50
今年的课程有很大一部分内容是cdq分治及其扩展(也就是二进制分组),拜读后觉得还是蛮有用的,这里小小地总结一下。(话说自己草稿箱里还有好多学习笔记的半成品呢,真是弱爆了。顺便感谢下fy与wxl向我介绍了那么好的东西) 推荐论文: 1 《从<Cash>谈一类分治算法的应用》 陈丹琦 2 《浅谈数据结构题的几个非经典解法》 许昊然 Q: cdq分治和普通的分治有什么区别? A: 在我们平常使用的分治中,每一个子问题只解决它本身(可以说是封闭的)。而在cdq分治中,对于划分出来的两个子问题,前一个子问题用来解决后一个子问题而不是它本身。 在很多问题中(比如大多数数据结构题),我们经常需要处理一些动态问题。然而我们对动态问题的处理总是不如静态问题来的那么方便,于是就有了cdq分治。但正如论文2所提到的,使用这算法的前提是问题必须具有以下两个性质: 1.修改操作对询问的贡献独立,修改操作互不影响效果。 2.题目允许使用离线算法。 具体算法流程如下: 1.将整个操作序列分为两个长度相等的部分(分) 2.递归处理前一部分的子问题(治1) 3.计算前一部分的子问题中的修改操作对后一部分子问题的影响(治2) 4.递归处理后一部分子问题(治3) {    cdq分治 的大概步骤就是 1. Solve(l,mid) 2. Solve(mid+1,r) 3. 处理左边对右边的影响 } 在整个流程中

CDQ分治学习笔记

假装没事ソ 提交于 2020-03-25 03:10:07
CDQ分治是一种神奇的分治算法 它的核心思想大致是这样的 将所有的操作看成一个区间 一个[L,R]的区间分为[L,mid][mid+1,R]这两个子问题 然后处理[L,mid]这个区间对[mid+1,R]这个区间的贡献 这样讲解十分抽象,我们还是来看几个实际问题 二维偏序问题:    给定N个有序对(a,b),求对于每个(a,b),满足a2<a且b2<b的有序对(a2,b2)有多少个 我们现将所有点排序按一维,这样右区间就不会对左区间产生影响,就可以CDQ了 在具体处理左边区间对右边影响时, 我们将[L,mid]和[mid+1,R]再分别按另一维排序 然后用两个指针扫一下,就好了 我们设想一下将二维的偏序加到三维: 其实做法是差不多的 一维排序,一维CDQ一维树状数组 具体处理时我们将[L,min],[mid+1,R]排序后用两个指针扫时 遇到左边小于右边的情况就将树状数组在a[i].z处加上1 然后在j处的答案就是sum(a[j].z) 上代码(洛谷3810) # include<iostream> # include<cstdio> # include<algorithm> # include<cmath> # include<cstring> using std::sort; inline int read() { int x=0; char ch=getchar();

「分治」-cdq分治

天大地大妈咪最大 提交于 2020-03-25 03:07:03
cdq分治是一种分治算法: 一种分治思想,必须离线,可以用来处理序列上的问题(比如偏序问 题),还可以优化1D/1D类型的DP。 • 算法的大体思路我们可以用点对来描述。假定我们有一个长度为n的序列,要处理序列中元素点对间的关系。定义一个操作cdq(l,r)表示当前处理序列上区间[L,R]的点对关系。那么我们需要找到[L,R]的中点M,将不同的点对分为三类: • A:两个点都在区间[L,M]上 • B:两个点都在区间[M+1,R]上 • C:两个点分别在[L,M]和[M+1,R]上。 对于前两种情况,分别用cdq(L,M),cdq(M+1,R)解决,也就是甩锅给下一层。 对于第三种情况,我们需要想方设法在当前层的cdq(L,R)中解决。 在解决当前层的问题时,利用已经处理好的左右两边的信息,具体视不同题目而 定。 其实,这样的算法很帅! 例题: A. 陌上花开 题目描述 有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),用三个整数表示。 现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。 定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。 这道题就是一道裸的cdq模板题,但是对于初学的菜鸡(就是我)来说还是异常的艰难,我们知道对于普通的二维偏序问题

CDQ分治【待补充,数据结构 - CDQ分治】

人盡茶涼 提交于 2020-03-25 02:59:10
为什么划分到数据结构Tag内呢,因为CDQ分治可以顶替复杂的高级数据结构,而且常数比较小。 待补充 相关资料 【教程】简易CDQ分治教程&学习笔记 CDQ学习笔记 权值树状数组/权值线段树 【算法讲堂】【电子科技大学】【ACM】CDQ分治 树状数组求逆序数 BZOJ 陌上花开 来源: https://www.cnblogs.com/shengwang/p/9857150.html

POJ 1741 Tree【Tree,点分治】

橙三吉。 提交于 2020-03-23 13:47:53
树上的算法真的很有意思……哈哈。 给一棵边带权树,问两点之间的距离小于等于K的点对有多少个。 将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。 如果我们已经知道了此时所有点到根的距离a[i], a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想,分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉(显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算)。 在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心,这样,所有的子树规模就会变的很小了。 时间复杂度O(Nlog^2N)。 树的重心的算法可以线性求解。 #include <cstdio> #include <algorithm> #include <vector> #include <cstring> using namespace std; #define N 10009 struct node { int v, l; node() {}; node(int _v, int _l): v(_v), l(_l) {}; }; vector<node> g[N]; int n, k, size, s[N

分治FFT

谁说我不能喝 提交于 2020-03-20 21:24:55
目录 分治FFT 目的 算法 代码 分治FFT 目的 解决这样一类式子: \[f[n] = \sum_{i = 0}^{n - 1}f[i]g[n - i]\] 算法 看上去跟普通卷积式子挺像的,但是由于计算 \(f\) 的每一项时都在利用它前面的项来产生贡献,所以不能一次FFT搞完。用FFT爆算复杂度 \(O(n^2logn)\) ,比直接枚举复杂度还高…… 考虑优化这个算法,如果我们要计算区间 \([l, r]\) 内的 \(f\) 值,如果可以快速算出区间 \([l, mid]\) 内的 \(f\) 值对区间 \([mid + 1, r]\) 内的 \(f\) 值产生了怎样的影响,就可以采取CDQ分治,不断递归下去算。 考虑 \(x \in [mid + 1, r]\) , \([l, mid]\) 给它的贡献是: \[h[x] = \sum_{i = l}^{mid}f[i]g[x - i]\] 为了方便,我们将范围扩充到 \([1, x - 1]\) (假设此时 \(f[mid + 1] ... f[r] = 0\) ),因此有: \[h[x] = \sum_{i = l}^{x - 1}f[i]g[x - i]\] 为了便于FFT计算,将枚举改成从0开始。(把表达式中的 \(i\) 改成 \(i + l\) ,因为原来的 \(i\) 等于现在的 \(i + l\) )

动态规划、回溯、贪心,分治

帅比萌擦擦* 提交于 2020-03-17 01:24:09
某厂面试归来,发现自己落伍了!>>> 动态规划篇 从斐波那契数列开始 我们先给出斐波那契数列的常用算法类 public class Fibonacci { private static int num = 0 ; private Fibonacci () { } public static int fib ( int n) { num ++ ; if (n == 0 ) { return 0 ; } if (n == 1 ) { return 1 ; } return fib (n - 1 ) + fib (n - 2 ) ; } public static void main (String[] args) { num = 0 ; int n = 20 ; long start = System. nanoTime () ; int res = Fibonacci. fib (n) ; long end = System. nanoTime () ; System. out .println(res) ; System. out .println((end - start) / 1000000000.0 ) ; System. out .println( num ) ; } } 运行结果 6765 3.96599E-4 21891 此时我们调大n的值为40 运行结果

POJ1741 tree 点分治

China☆狼群 提交于 2020-03-12 07:33:26
题意:给定一棵树,求树中长度小于等于K的路径条数。 题解: 分治重在思想,没有固定的算法——对于本题中某一子树上的任意一条路径,其要么是经过该子树的根,要么不经过,不经过的情况分治到子树上,我们只用考虑经过子树的根的情况。 由于经过了根节点,所以该路径的起始点一定是在根节点的两颗子树上,但直接求解方案数较为困难,我们可以进行如下转化: |dist(i,j)<=k且i,j不在同一棵子树上|=|dist(i,j)<=k|-Σ|dist(i,j)<=k且i,j在同一棵子树上| 第一项把所有子节点到根的距离放在一起排序后可以用O(n)的时间解决,第二项把该子树的某一子树中所有子节点到根的距离放在一起排序后也可以用O(n)的时间解决。 为了防止出现一条链的情况,每次分治子树的时候从树的重心向下分治,这样就能将总的分治层数降到logN 而每一层最多向下扩展N个节点,因此瓶颈就是排序算法的选择,使用快速排序是NlogN,而基数排序可以降到N,因此总的复杂度使用快排就是Nlog^2N #include <cstdio> #include <climits> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int MAXN=30000+2

分治思想与归并排序

主宰稳场 提交于 2020-03-09 22:00:04
分治法 的思想: 将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些自问题,然后再合并这些自问题的解来建立原问题的解。 分支模式在每层递归时都有三个步骤: 分解 原问题为若干子问题,这些子问题是原问题的规模较小的实例。 解决 这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。 合并 这些子问题的解成原问题的解。 归并排序 算法完全遵循分治模式。直观上其操作如下: 分解: 分解待排序的n个元素的序列成各具n/2个元素的两个子序列。 解决: 使用归并排序递归地排序两个子序列。 合并: 合并两个已排序的子序列以产生已排序的答案。 参考: 《算法导论》中文版原书第3版Page16,17 来源: https://www.cnblogs.com/drfxiaoliuzi/p/5892158.html