主席树

「BZOJ 2653」middle「主席树」「二分」

旧时模样 提交于 2019-11-27 13:42:50
题意 一个长度为 \(n\) 的序列 \(a\) ,设其排过序之后为 \(b\) ,其中位数定义为 \(b[n/2]\) ,其中 \(a,b\) 从 \(0\) 开始标号,除法取下整。给你一个长度为 \(n\) 的序列 \(s\) 。回答 \(Q\) 个这样的询问: \(s\) 的左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子序列中,最大的中位数。其中 \(a<b<c<d\) 。位置也从 \(0\) 开始标号。强制在线。 题解 比较套路地,我们考虑二分这个中位数(设为当前 \(mid\) ),如果它偏左就往右移,否则往左移 为了方便,若 \(x<mid\) ,它的贡献是 \(-1\) ,否则是 \(1\) ,这样我们只要看总贡献的正负就行 我们就求出 \([b + 1, c - 1]\) 的贡献,加上 \([a, b]\) 的最大后缀和 \([c, d]\) 的最大前缀 我们考虑怎么求一个区间 \([l, r]\) 对 \(c\) 的贡献的前缀max,后缀max,区间和。如果对下标开主席树,区间和可以,但前两个操作不太行。 考虑两维互换。先考虑 \([1, n]\) 对 \(1\) (注意这里已经离散化过了)的贡献,然后再移动到 \([1, n]\) 对 \(2\) 的贡献。我们发现总共只有 \(n\) 次 \(1\) 被改成 \(-1\) 的操作

模板 - 主席树

夙愿已清 提交于 2019-11-27 13:02:43
像平衡树一样,只不过左子树表示的是[l,mid]的所有值,右子树表示的是[mid+1,r]的所有值,一般是维护一个cnt就可以类似在平衡树上面二分出第k小,也可以求出第k小的值在主席树中是哪个元素。当然根据你的想象力,所有只跟左子树或者右子树单侧相关的都可以找。 来源: https://www.cnblogs.com/Yinku/p/11364332.html

[lct][最小生成树][主席树] Bzoj P4046 Pork barre

限于喜欢 提交于 2019-11-27 10:43:05
Description Winning the election was simpler than you expected: it was enough to promise to finally build a good quality, country-wide road infrastructure, of course without crippling the budget_ Your happiness did not last long, however: it seems, that the citizens have found a way to actually hold you accountable for your promise! There are n major cities in your country. The Ministry of Transport has prepared a detailed map, outlining m possible highway connections, together with their costs. The Quality Assurance Committee will not let you build a highway cheaper than l, and the National

蒟蒻林荫的小复习——主席树

孤人 提交于 2019-11-27 08:53:28
主席树也就是指可持久化线段树,大致思想也就是每次利用之前的重复信息,只为被更新的一部分开辟新点。而且所谓可持久化线段树实际上是指可持久化权值线段树,线段树中每个端点存的是这个端点所代表的树的出现次数。 而在主席树的维护当中对于每个历史版本如果都开一颗新树,那么M将是最终的结局。正确解法则是为每一个改变的点分配编号而未改变的点直接链接到新树上。 下面就来从主席树的两个经典问题来考虑 1:区间第K小问题 传送门 首先分析问题,给出一个序列,每次给定一个封闭区间,求这个封闭区间以内的最小值。 上面我们说过,主席树实际上是一颗权值线段树,点里面存的是点所代表这个数出现了多少次。先看数据范围,离散化是跑不了的。反正n<=2*10^5 那么就把原数列离散化到1---2*10^5的区间之内。开n棵树,第i颗代表压入前i个数之后各个数字的出现情况。 声明一个3元组,分别代表左儿子编号,右儿子编号,当前节点子树内所有元素共计出现次数(如果是叶子节点就代表叶子节点代表的这个元素出现次数) 所以说动态建树不用解释了吧 int build(int l,int r) { int rt=++tot; if(l<r) { L[rt]=build(l,mid);//每个节点构造线段树中左子树位置 R[rt]=build(mid+1,r);//每个节点构造线段树中右子树位置 } return rt; } build

博客学习链接

ε祈祈猫儿з 提交于 2019-11-27 05:01:09
记录了我学习算法期间读过的特别好的博客 主席树 主席树 静态 主席树 动态 字典树 简单实现 求逆元 回文树 回文树集训队论文 回文树 模板 树状数组+无序第K小 后缀数组+最长不重叠重复子串 网络流 特别好的博客 关于dinic当前弧优化 扩展kmp kmp模板 来源: https://blog.csdn.net/weixin_43846076/article/details/98955632

主席树 动态 模板

可紊 提交于 2019-11-27 05:00:08
自己整理模板,仅作模板保存使用 # include <bits/stdc++.h> using namespace std ; const int maxn = 6e4 + 5 ; //主席树最多需要在原空间上开大40倍,原空间本身首先要加大,这题原空间为50000 const int maxm = 1e4 + 5 ; int T [ maxn ] , S [ maxn ] , L [ maxn * 40 ] , R [ maxn * 40 ] , sum [ maxn * 40 ] ; //S是树状数组,L,R是线段树,sum是前缀和 int sz [ maxn ] , h [ maxn ] ; //sz为原数组,h为离散化后数组 int ul [ maxn ] , ur [ maxn ] ; int tot , num , n , q ; struct node { int l , r , k ; bool flag ; //ture代表Q,false代表C } Q [ maxm ] ; //存储询问 void build ( int & rt , int l , int r ) { rt = ++ tot ; sum [ rt ] = 0 ; if ( l == r ) return ; int mid = ( l + r ) >> 1 ; build ( L [ rt ]

主席树学习心得

China☆狼群 提交于 2019-11-27 01:31:30
主席树是用于处理区间第k大(小)的问题 对于一个序列,想要知道[l,r]区间内第k大(小)的数,考虑建一颗权值线段树,这样我们就可以解决一个定区间的第k大问题了,假如我现在有两颗权值线段树,分别维护了[1,l-1],[1,r]两个区间,这样要解决[l,r]区间的问题,就是两个权值线段树节点值作差,就可以得到一颗维护[l,r]的权值线段树了,那么,如何对于1<=l,r<=n的区间得到对应的维护[l,r]的权值线段树呢?显然我们只需维护所有的[1,i],1<=i<=n,这样我们可以通过作差得到任意维护[l,r]区间的权值线段树了,然而,建立n个权值线段树,它的时间空间花费都是无法接受的,不过,我们可以发现,相邻的两颗权值线段树,他们只有一条链是存在差异的,也就是说,我们从维护[1,i]的权值线段树,变成[1,i+1]的权值线段树,只需额外开辟logn的空间,这样,我们创建两个指针,一个指向前一颗权值线段树的根,另外一颗指向当前生成的权值线段树的根,如果新加入的数,只会影响前一颗权值线段树的右儿子(左儿子同理),那么我们让当前生成的权值线段树,左儿子指向前一颗权值线段树的左儿子,然后为右儿子开辟新节点,两个指针同时下移到他们的右儿子,进行递归处理即可,这样我们就构造了一颗主席树,这就是动态开点的过程 (图源洛谷) 建立主席树后,查询[l,r]的区间第k大(小问题)就变成了在主席树上查询

进化版数据结构(可持久化)

牧云@^-^@ 提交于 2019-11-26 20:34:58
·线段树 主席树和可持久化线段树有什么区别? 主席树(可持久化线段树) 可持久化线段树(Persistent data structure)最主要的功能就是可以查询历史版本。那么presistent≈president(主席),得名主席树。 给你个问题: 给你一段数列,要求查询一段区间的第k小数。 \((n<=10^5)\) 要怎么做?【面面相觑】 很容易想到两种方法: ①建一棵线段树,然后再每个表示区间的节点上都建一棵 权值线段树 !直接查询即可。 ②建n棵线段树,第i棵线段树表示1~i里面所有的数构成的 权值线段树 !那么查询区间的时候就直接像使用前缀和一样,每个节点表示的权值区间在这个查询的区间中拥有数的个数就是:当前节点个数减去区间左端点建的树中对应的点的数量。 例如: 我们查询区间[2~4]的第k小,我们可以很显然的地得出:每个节点的值其实就是在第4棵线段树上这个节点的值减去在第一棵线段树上这个节点的值。然后按照权值线段树的查询规律下去找就好了。 很明显这个算法的时间空间复杂度都是 \(O(n^2\) \(log\) \(n)\) 的。 主席树的主体是线段树,准确的说,是很多棵线段树。那么如何既能建出那么多棵线段树,同时不会MLE、TLE. 很显然我们会发现,修改前缀和的时候只有可能加入一个数,而受到这个数影响的只有可能有一条链,那么其他我们新开的点就都是废的了

SCUT106 花式AC 主席树版本

左心房为你撑大大i 提交于 2019-11-26 20:29:42
网址: https://scut.online/p/106 题意: 给出一个点权树以$1$为根节点,求子树有几个节点的权值小于等于$k$。 题解: 主席树版本,先对树$dfs$求出第一次进入某节点的时间戳,然后同时处理出子树的节点数,然后求出的$dfs$序中某节点的时间戳后长度为该节点的子树的大小$-1$就是这个节点的子树对应的序列,在主席树上求个数即可。原理类似于求区间第k大,只不过是每棵主席树都保存了序列前$m(1 \leq m \leq n)$个数大小是$[1,n]$数的总个数,然后查询时查询区间中的$[1,k]$的数的个数即可。由于本题数值过大,因为权值是离散化的,所以输入的$k$也要离散化,找到小于等于k的第一个数。 AC代码: #include <bits/stdc++.h> using namespace std; const int MAXN=100005; struct cheiftree { struct node { int l,r,sum; }; node tr[MAXN*20]; int rt[MAXN]; int cnt=0; void init() { cnt=0; } void build(int &rt,int l,int r) { rt=++cnt; tr[rt].sum=0; int m=(l+r)/2; if(l==r) return;

SCUT - 106 - 花式ac - 主席树

£可爱£侵袭症+ 提交于 2019-11-26 14:57:06
https://scut.online/p/106 错在这组样例,发现是离散化之后,对k访问的时候也是应该访问离散化之后的k。 12 4 1 1 2 2 5 5 4 4 3 3 2 1 1 3 3 5 7 7 9 9 9 11 11 1 10 3 10 3 11 2 4 发现主席树大概还真的要开够log倍,少一点都不行,那干脆开大一点。 #include<bits/stdc++.h> #define mid ((l+r)>>1) using namespace std; const int MAXN = 100000 + 5; int a[MAXN], b[MAXN]; vector<int> E[MAXN]; int siz[MAXN], tid[MAXN], rnk[MAXN], cnt; int T[MAXN], tcnt; int sum[MAXN << 5], L[MAXN << 5], R[MAXN << 5]; void init(int n) { for(int i = 1; i <= n; ++i) E[i].clear(); cnt = 0; tcnt = 0; } void dfs(int u) { siz[u] = 1; tid[u] = ++cnt; rnk[cnt] = u; for(auto v : E[u]) { dfs(v); siz[u] +=