主席树(模板)
求区间第K大的值; 我们需要在短时间内回答数目巨大的问题,这个算法的核心是空间换时间; 每个点建一个线段树,是的; 我们先离散化所有权值,使得当前的权值在1到n范围内,恰巧是vector里的下标; 对于每一个点,我们分成左二子和右儿子,分别存放当前区间的左半部分和右半部分,维护左右节点的数量; 我们怎么求区间第K大呢? 运用前缀和的思想,我们其实建出来的很多树左右数量是递增的; 遍历每一个节点,现将当前节点继承前一节点的历史状态,再将节点数+1,根据当前节点的大小判断插在左边还是右边,递归进行此操作; ? 对于区间【l,r】,我们将l-1的树拿出来,r的树拿出来,每个树的节点数就是1到 i 的节点数,先两个左子树的节点数相减,得到的数与当前k比较, 如果大于k就在左子树里找,如果小于k就在右子树里找k-sum_L个;也是递归实现; ? 实质就是每次加点,查询时套用前缀和,根据数量判断向下递归的方向,从而减少时间复杂度; 时刻注意每个点代表的历史状态,谁和谁对应; ? 离散化操作 sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
先将数组排序,然后unique(),此时函数操作是将重复的数字都扔到后面,返回不重复序列的最后一个数的下一个数的下标;这时我们将后面删去即可; 取得离散后的数lower_bound即可; 根据学长的传承,空间开40倍; ? section (区间) #include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; const int maxn=2e5+10; vector<int> v; int root_sec[maxn]; struct node { int l,r,sum_pos; }t[maxn*40]; int get_id(int x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1; } int n,m,a[maxn],cnt; void update(int l,int r,int x,int &y,int pos) { y=++cnt;t[y]=t[x];t[y].sum_pos++; if(l==r) return ; int mid=(l+r)>>1; if(pos<=mid) update(l,mid,t[x].l,t[y].l,pos); else update(mid+1,t[x].r,t[y].r,pos); } int query_id(int l,int y,int sum_k) { if(l==r) return l; int sum_sec=t[t[y].l].sum_pos-t[t[x].l].sum_pos; int mid=(l+r)>>1; if(sum_sec>=sum_k) return query_id(l,sum_k); else return query_id(mid+1,sum_k-sum_sec); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); v.push_back(a[i]); } sort(v.begin(),v.end()); for(int i=1;i<=n;i++) update(1,n,root_sec[i-1],root_sec[i],get_id(a[i])); for(int i=1;i<=m;i++) { int l,k; scanf("%d%d%d",&l,&r,&k); printf("%dn",v[query_id(1,root_sec[l-1],root_sec[r],k)-1]); } return 0; } ? ? ? ? ? ? 至于为什么叫主席树,是因为发明他的人叫HJT; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |