区间第K值——主席树详解
来源:互联网 发布:万税 知乎 编辑:程序博客网 时间:2024/05/21 10:42
序:这是一篇迟到的题解,机房的小伙伴们系统地学主席树应该是七月份的时候,然而我没赶上趟,当时压根没看懂主席树是什么东东。 昨天晚上决定重新来过,于是请教了一位大神1113(这是他的博客,不过好像因为手机验证的原因很久没有更新了),他告诉我了主席树的始末,然后我就秒懂了,原来并没有想象中的那么复杂,相信看完了这篇题解,你也会这么觉得的。下面开始正文:
Description
给定一个长度为
Solution
对于一个问题,我们一般采取的方法就是化繁为简,这样更容易分析题目的特点,然后逐个击破。对于问题中的
对于一个询问
void solve(){ //离散(未写出) //计数,用cnt计数 for(int i=L;i<=R;i++)cnt[A[i]]++; //维护后缀和,len是unique之后数组长度 for(int i=len;i>=1;i--){ cnt[i]+=cnt[i+1]; if(cnt[i]>=K){ //找到第K大数 Pt(tmp[i]); putchar('\n'); break; } }}
上面方法的复杂度是
对于上面一次询问
struct Segment_Tree{ struct node{ int L,R,sum;//sum表示[L,R]中元素总数 }tree[N<<2]; int find(int k,int p){ if(tree[p].L==tree[p].R)return tree[p].L; if(tree[p<<1|1].sum>=k)//右区间元素个数大于或等于k,就往右区间找 return find(k,p<<1|1); return find(k-tree[p<<1|1].sum,p<<1);//往左区间找 }}T;
虽然说查询只有
Code
#include<stdio.h>#include<algorithm>#include<iostream>#define N 30005#define M 30005using namespace std;template <class T>inline void Rd(T &res){ char c;res=0;int k=1; while(c=getchar(),c<48&&c!='-'); if(c=='-'){k=-1;c='0';} do{ res=(res<<3)+(res<<1)+(c^48); }while(c=getchar(),c>=48); res*=k;}template <class T>inline void Pt(T res){ if(res<0){ putchar('-'); res=-res; } if(res>=10)Pt(res/10); putchar(res%10+48);}struct opr{ int L,R,k;}Q[M];int n,m,len;int A[N],tmp[N];struct Segment_Tree{ struct node{ //L是左区间编号,R是右区间编号,sum是当前区间的元素个数 int L,R,sum; }tree[N*15]; int tot,root[N];//tot用来动态开节点,root存第i棵前缀线段树的节点编号 void build(int L,int R,int &p){ //[L,R]表示当前区间,p是当前节点编号 p=++tot; tree[p].sum=0; if(L==R)return; int mid=(L+R)>>1; build(L,mid,tree[p].L); build(mid+1,R,tree[p].R); } void insert(int t,int L,int R,int x,int &p){ //t是原来节点编号,p是当前节点编号,[L,R]表示当前区间,x是要加的位置 p=++tot; tree[p]=tree[t]; tree[p].sum++; if(L==R)return; int mid=(L+R)>>1; if(x<=mid)insert(tree[p].L,L,mid,x,tree[p].L); else insert(tree[p].R,mid+1,R,x,tree[p].R); } int find(int t,int L,int R,int k,int p){ //t表示第L-1棵线段树的节点,p表示第R棵线段树的节点,[L,R]表示当前区间 if(L==R)return L; int mid=(L+R)>>1; int cnt=tree[tree[p].R].sum-tree[tree[t].R].sum; if(cnt>=k)return find(tree[t].R,mid+1,R,k,tree[p].R); return find(tree[t].L,L,mid,k-cnt,tree[p].L); } Segment_Tree(){tot=0;}}T;void solve(){ //建树 T.build(1,len,T.root[0]); for(int i=1;i<=n;i++) T.insert(T.root[i-1],1,len,A[i],T.root[i]); //查询 for(int i=1;i<=m;i++){ int L=Q[i].L,R=Q[i].R,k=Q[i].k; Pt(tmp[T.find(T.root[L-1],1,len,k,T.root[R])]); putchar('\n'); }}int main(){ Rd(n);Rd(m); for(int i=1;i<=n;i++){ Rd(A[i]); tmp[i]=A[i]; } for(int i=1;i<=m;i++){ Rd(Q[i].L);Rd(Q[i].R);Rd(Q[i].k); } //离散 sort(tmp+1,tmp+n+1); len=unique(tmp+1,tmp+n+1)-tmp-1; for(int i=1;i<=n;i++) A[i]=lower_bound(tmp+1,tmp+len+1,A[i])-tmp; solve(); return 0;}
- 区间第K值——主席树详解
- 主席树——区间第K大
- 浅谈主席树和区间第k值
- 主席树 --- 求区间第k大值
- 区间第k大值(主席树入门)
- 主席树模板 区间第k小
- poj2104 主席树区间第k大
- 主席树解决区间第k大
- 区间第k大(主席树)
- 询问区间第k大(小)——主席树
- 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】
- 主席树详解 zoj 2112 (单点更新)+ poj 2104 区间第k大
- 主席树 求区间第k大数(可修改)
- poj 2104 区间第K小 主席树入门题
- poj 2104 区间第k大 主席树
- poj2761&&poj2104 主席树(静态区间第K大)
- zoj-2112(主席树动态求区间第k小数)
- hdu 2665 区间第K大 主席树入门
- Android进程优先级
- JVM内存区域-栈的理解
- itertoors
- AEC、AGC、ANS在视音频会议中的作用?
- STL中的set和map
- 区间第K值——主席树详解
- 面向对象
- 静态内部类、局部内部类可访问的变量限制
- 面试总结
- 一切都会过去的
- POJ
- sublime中css输入分号后自动提示的烦恼
- iOS应用内购买项目(IAP)
- Bootstrap框架----新建示例--各种input