参考:优秀的B站视频;

    和 https://blog.csdn.net/creatorx/article/details/75446472

感觉主席树这个思路是真的优秀,每次在前一次的线段树的基础上建立一颗新的小线段树;所以更新和查询都是要前后两个根节点进行操作;

利用引用,只用修改此次的节点,而不动前一次的线段树;

主席树可用在求区间的第K大的数上:思路是:

我们也可以利用前缀和这个思想来解决建树这个问题,我们只需要建立n颗“前缀”线段树就行,第i树维护[1,i]序列,这样我们处理任意区间l, r时就可以通过处理区间[1,l - 1], [1,r],就行,然后两者的处理结果进行相加相减就行。为什么满足相加减的性质,我们简单分析一下就很容易得出。如果在区间[1,l - 1]中有x个数小于一个数,在[1,r]中有y个数小于那个数,那么在区间[l,r]中就有y - x 个数小于那个数了,这样就很好理解为什么可以相加减了,另外,每颗树的结构都一样,都是一颗叶节点为n个的线段树。

这里还有一个利用vector离散化的操作;

hdu ac的:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
#define pb push_back
const int maxn = ; struct node {
int l,r;
int sum;
}T[maxn*];
int a[maxn],root[maxn];
vector<int>v;
int getid(int x){
return lower_bound(v.begin(),v.end(),x) - v.begin() + ;
}
int n,m,cnt,x,y,k; void init(){
v.clear();
memset(T,,sizeof(T));
cnt = ;
}
void update(int l,int r,int &x,int y,int pos)
{
T[++cnt] = T[y]; T[cnt].sum++; x = cnt;
if(l==r)return;
int mid = (l+r)>>;
if(mid>=pos)
update(l,mid,T[x].l,T[y].l,pos);
else update(mid+,r,T[x].r,T[y].r,pos);
} int query(int l,int r,int x,int y,int pos)
{
if(l==r)return l;
int sum = T[T[y].l].sum - T[T[x].l].sum;
int mid = (l+r)>>;
if(sum >= pos)
return query(l,mid,T[x].l,T[y].l,pos);
else return query(mid+,r,T[x].r,T[y].r,pos - sum);
}
int main(){
int t;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
for(int i=; i<=n; i++)
{
scanf("%d", &a[i]);
v.pb(a[i]);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(),v.end()),v.end()); for(int i=; i<=n; i++)
update(,n,root[i],root[i-],getid(a[i]));
for(int i=; i<=m; i++)
{
int le,ri,k;
scanf("%d%d%d", &le,&ri,&k);
printf("%d\n",v[query(,n,root[le-],root[ri],k)-]);
}
}
return ;
}

POJ的

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
#define pb push_back
const int maxn = ; struct node {
int l,r;
int sum;
}T[maxn*];
int a[maxn],root[maxn];
vector<int>v;
int getid(int x){
return lower_bound(v.begin(),v.end(),x) - v.begin() + ;
}
int n,m,cnt,x,y,k; void update(int l,int r,int &x,int y,int pos)
{
T[++cnt] = T[y]; T[cnt].sum++; x = cnt;
if(l==r)return;
int mid = (l+r)>>;
if(mid>=pos)
update(l,mid,T[x].l,T[y].l,pos);
else update(mid+,r,T[x].r,T[y].r,pos);
} int query(int l,int r,int x,int y,int pos)
{
if(l==r)return l;
int sum = T[T[y].l].sum - T[T[x].l].sum;
int mid = (l+r)>>;
if(sum >= pos)
return query(l,mid,T[x].l,T[y].l,pos);
else return query(mid+,r,T[x].r,T[y].r,pos - sum);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=; i<=n; i++)
{
scanf("%d", &a[i]);
v.pb(a[i]);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(),v.end()),v.end()); for(int i=; i<=n; i++)
update(,n,root[i],root[i-],getid(a[i]));
for(int i=; i<=m; i++)
{
int le,ri,k;
scanf("%d%d%d", &le,&ri,&k);
printf("%d\n",v[query(,n,root[le-],root[ri],k)-]);
} return ;
}

最新文章

  1. [CC]DgmOctree—执行Cell遍历和单元计算
  2. Linux下安装php加速软件Xcache
  3. git 常用的简单命令
  4. 第一个Android程序
  5. Linux启动过程中几个重要配置文件的执行过程
  6. Snappy压缩
  7. [偏微分方程教程习题参考解答]4.1Duhamel 原理
  8. Java log code example
  9. hibernate--could not initialize proxy - no Session--懒加载问题
  10. action找不到
  11. AngularJS数据建模(转载)
  12. clob字段的值插入和查询N种方法【包括java调用存储过程传入clob参数】
  13. js关于“变量提升、作用域、私有作用域等知识点”高级解题思路
  14. centos 7.5+如何格式化硬盘
  15. Windows 操作系统
  16. Nginx服务器的rewrite、全局变量、重定向和防盗链相关功能
  17. deepin使用笔记-解决安装并解决gvim没有启动器的问题
  18. Handler实现与机制 &amp;&amp; Blocking Queue &amp;&amp; IdleHandler使用
  19. php email
  20. iOS开发-Launch Image和Launch Screen

热门文章

  1. 箭头函数=&gt;
  2. Linux系统命令。
  3. jumpserver1.4.1 安装过程
  4. Gridea+GitHub搭建个人博客
  5. Activity 使用详解
  6. 探秘最小生成树&amp;&amp;洛谷P2126题解
  7. Vue系列:为不同页面设置body背景颜色
  8. eclipse项目导入idea jdk版本不一致&#128565;
  9. CSS实现三栏布局(5种)
  10. 动图+源码,演示Java中常用数据结构执行过程及原理