主席树

hdu6621--主席树

耗尽温柔 提交于 2019-11-30 01:44:29
K-th Closest Distance 题意:给定长度为 \(n\) 的数列,现有m次查询,每组询问给 \(l,r,p,k\) ,问对 \(l<=i<=r\) , \(|p-a[i]|\) 的第k小值。数据强制在线。 题解:二分答案ans,用主席树查询 \(a[l]\) 到 \(a[r]\) 之间 \([p-ans,p+ans]\) 的个数,个数为k即为答案。 ​ 由于强制在线,所以建主席树不能离散化。可以动态空间直接建树。 #include<bits/stdc++.h> using namespace std; const int maxn=100005; int n,m; const int N=1e6; struct node{ int ls,rs,sum; }; node tree[maxn*22]; int rt[maxn],tot=0; int update(int l,int r,int x,int pre){ int now=++tot; tree[now]=tree[pre]; if(l==r){ tree[now].sum++; return now; } int mid=(l+r)>>1; if(x>mid) tree[now].rs=update(mid+1,r,x,tree[pre].rs); else tree[now].ls=update(l

主席树

笑着哭i 提交于 2019-11-29 11:53:54
https://blog.csdn.net/qq_39653331/article/details/78816987 https://www.cnblogs.com/zyf0163/p/4749042.html 感觉都写的好好,但是还是没有完全弄懂,目前只是懂了一点点QAQ,还是太菜了 然后贴上最近刷的几道题的代码吧 hdu 4417 Super Mario 1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define N 100005 8 9 int root[N];//root[i]表示第i课线段树 10 int size[N*25],lchild[N*25],rchild[N*25]; 11 int tot; 12 13 void insert(int last,int cur,int L,int R,int k) //单点更新 14 { 15 size[cur]=size[last]+1;//将前一个树的信息复制过来 16 lchild[cur]=lchild[last]; 17 rchild[cur]=rchild[last]; 18 if(L==R)return ;

2019-9-12做题记录

﹥>﹥吖頭↗ 提交于 2019-11-29 11:28:01
1、Peaks加强版(Kruskal重构树,主席树) $Kruskal$重构树的模板题。 $Kruskal$重构树:在合并两个联通块的时候,建一个新点,当原来两个代表元的父亲,作为新联通块的代表元。 查询的时候在叶子上跳到能到达的最远祖先,在叶子的$dfs$序里面查$k$大就行了。 2、 【 LOJ#6073】距离(主席树) 树链剖分$+$可持久化的套路。 3、 【 BZOJ4771】七彩树(主席树) 好听的名字和好学的题目。 按照虚树的思想,我们把同一颜色的点拎出来,按$dfs$序排序,然后在每个点$+1$,在相邻点$LCA$处$-1$,这样任意时候我们只要查子树和,就知道了子树里的颜色数。 如果不离线,我们直接按深度优先搜索序插入线段树,维护子树和。把线段树变成主席树,就可以在线了。 4、 【 BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增) 又是蛤省省选神仙题。 很显然贪心是从左到右能选就选。 对$sa$数组建主席树,值域是它原来的位置。 询问的时候先$rmq+$二分确定区间,找到第一个合法的,再在主席树上反复找后继,这样是$O(n/|P|*logn)$的,当$|P|$很小的时候是不行的。 当$P\le 50$,我们可以预处理答案。直接对每一个$|P|$构建一棵树,每一个位置向它的前驱(一个位置$x$,使得向后$|P|$位后形成的字符串和当前位置向后

主席树学习笔记

谁都会走 提交于 2019-11-29 08:29:32
感性的理解,主席树就是多个阶段下(即可保存多个版本)的多棵线段树(可以是普通线段树也可以是权值线段树)。 一、权值线段树 普通线段树相信大家都会,这里先简述一下权值线段树。 顾名思义,权值线段树的每个节点带的[l,r]表示的是权值区间l~r,而不是像普通平衡树上那样表示在序列上的第l个位置~第r个位置。而每个节点的val值表示该权值区间内有多少个数。比如对于1,4,1,6,4,4,2这样一个序列,可以表示为下图: 对于插入操作,直接从根节点往下找到所有权值范围包括k的节点,将其val++,一直到叶子节点。 对于查询操作,如查询第k小的数,即从根节点开始,若 左子树的val<=k ,就往左子树继续查找,否则将 k-=左子树的val 并往右子树查找(显然该范围的第k小就是其右子范围的第 k-tre[tre[x].ls].val 小)。 应该是比较好理解的。 但是,有一个很严重的问题,比如说1,10^6,10^7这样一个序列,明明只有三个元素,但是对于权值线段树,我们是要开 2*(10^7) 的节点的,显然空间上是无法接受的。怎么办呢? 这里讲的是 离散化+动态开点。 首先离散化,就是用每个权值在序列中的排名来代替该权值在序列中的位置。 其次是动态开点。在普通线段树中,我们建树、插入、查询的时候都是直接走的 rt<<1 或者 rt<<1|1 ,但是我们发现,有些节点是不需要的

主席树之初见

时光总嘲笑我的痴心妄想 提交于 2019-11-29 02:19:12
•何为主席树          图1 主席树的构造如图,以前序遍历的方式编号,叶子表示1到n 因为叶子是1到n,就有了左子树总是小于右子树的性质 除叶子外的节点记录的是区间sum代表这个节点的叶子有多少个数 如图 区间[2,2]有1个数,区间[3,3]有1个数 所以区间[1,2]有1个数,区间[3,4]有2个数,区间[1,4]有3个数 来源: https://www.cnblogs.com/MMMinoz/p/11440109.html

hdu 6704 后缀数组+主席树+RMQ

戏子无情 提交于 2019-11-29 01:05:21
给一个长为n的字符串,m次询问,每次求子串[l,r]第k次出现的起点位置 做法: 数据量很大,输入的字符串/询问总量可以达到1e5*5,必须尽量实现单次$O(logn)$的查询和至多$O(nlogn)$的预处理 1.子串[l,r]一定是某个后缀的前缀,而"后缀的前缀的重复出现"这个问题可以很容易想到后缀数组的height 2.考虑重复出现,显然一个后缀的长为r-l+1的前缀的出现位置即为height数组上一段连续的大于等于r-l+1的区间 3.得到这个区间后,就变成了一个在sa数组上求区间第k小的问题了 那么做法就是: 预处理: 1.预处理height数组 2.预处理height数组区间min的st表 3.预处理支持查询sa数组上的区间kth的 一棵可持久化线段树 处理询问: 1.我们从后缀数组的rk[l],直接锁定一个合法的起始位置, 2.然后利用st表快速二分出一个合法的连续区间,满足min>=r-l+1, 3.在可持久化线段树上查询这个区间的kth小 总复杂度:$O((m+n)logn)$ (说起来挺复杂,但是也就130行) #include<bits/stdc++.h> #define ll long long #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define rep(ii,a,b)

hdu-6703 array(主席树+set)

北城以北 提交于 2019-11-28 21:54:27
主席树维护最小值 如果一个数被插入队列,相当于这个数无法被选 inf 如果一个数加1e7;相当于这个数又可以被选 实际维护+1e7的操作比较麻烦 直接用将+1e7的数字压入set中二分找>=k的最小的数在于查找的答案去min #include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<set> #include<map> #include<vector> #include<bitset> #define inf 0x3f3f3f3f #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define sd(x) scanf("%d",&(x)) #define sl(x) scanf("%lld",&(x)) #define slf(x) scanf("%lf",&(x)) #define scs(s) scanf("%s",s) #define rep(i,a,b) for(int i=a;i<=b;i++) #define per(i,a,b) for(int i=a;i>=b;i--)

主席树

夙愿已清 提交于 2019-11-28 19:44:16
推荐博客: https://blog.csdn.net/pengwill97/article/details/80920143 https://www.cnblogs.com/AKMer/p/9956734.html 模板:求区间静态第k大 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2e5+10; 4 struct node{ 5 int l,r,num; 6 }tree[maxn*100]; 7 int size=0,a[maxn],refl[maxn],root[maxn]; 8 void update(int &x,int y,int l,int r,int dex){ 9 x=++size; 10 tree[x]=tree[y]; tree[x].num++; 11 if(l==r) return ; 12 int mid=(l+r)>>1; 13 if (dex<=mid) update(tree[x].l,tree[y].l,l,mid,dex); 14 else update(tree[x].r,tree[y].r,mid+1,r,dex); 15 } 16 int query(int x,int y,int l,int r,int kk){ 17 if (l==r)

K-th Number Poj - 2104 主席树

南笙酒味 提交于 2019-11-28 19:27:57
K-th Number Poj - 2104 主席树 题意 给你n数字,然后有m次询问,询问一段区间内的第k小的数。 解题思路 这个题是限时训练做的题,我不会,看到这个题我开始是拒绝的,虽然题意清晰简单,但是真的不会。限时结束后,学长说这个题是简单的主席树的入门题,我没学过啊。 如果你也没有学过的话,建议看我的另一篇博客,上面有自己的总结和一些博客推荐,就不用自己一个一个找了, 点我进去 。 哦, 这个题是主席树的模板题。 代码实现 #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int maxn=1e5+7; int root[maxn], a[maxn], x, y, k; int n, m, cnt; struct node{ int l, r, sum; //左子树的编号,右子树的编号,区间内的标记的数的个数 }t[maxn*40]; vector<int> v; int getid(int x) //需要进行离散化,因为主席树叶子端点上就是一个数。 { return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; //这里用vector进行的离散化,比较简单 }

主席树入门+博客推荐

风流意气都作罢 提交于 2019-11-28 19:27:20
主席树入门+博客推荐 学习总结 主席树是就是多颗线段树的总结 主席树的结构体中的 \(l\) 和 \(r\) 代表的是这个节点的左右子节点的标号,因为空间优化的原因,他们可能不再符合左子树编号等于 \(rt<<1\) ,右子树编号等于 \(rt<<1|1\) ,这也是我开始比较困惑的一点。在学习主席树之前,需要你很熟悉线段树这个东西,因为主席树的主体是多颗线段树,首先我们来简单的回顾一下线段树的简单特点和性质,我们熟悉的线段树一般是用一个结构体表示一个节点,每个节点有一个编号,节点里面一般有两个变量l, r来表示这个节点维护的区间,然后还有一个区间信息(比如区间最大值,最小值,和等,视具体问题而定),当然如果涉及到区间更新,还有一个add或者lazy叫做延迟标记的东西,然后一般线段树最明显的特点就行,一个父节点的编号是i,那么他的两只儿子节点的编号分别为2 * i(左) , 2 * i + 1(右),注意主席树在这一点有别于一般的线段树,每一个父节点,他的两个儿子节点的编号不一定满足这个关系。 主席树的结构体数组需要开多大呢?根据主席树的空间复杂度(这个我不会,我看的博客上的)是 \(n*logn\) ,看的博客上右开 \(n\) 的 20倍和40倍的,这里还是最好算一下,开40倍就很大了。 这里附上一个简单题目的代码 POJ 2104 K-th Number