题目:
题解:
主席树裸题?下方普及向见咯
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100005;
int root[N],a[N],b[N],p[N],sz,s;
struct hh{int l,r,w;}tree[N*80];
int cmp(int x,int y){return a[x]<a[y];}
void insert(int &now,int l,int r,int x)
{
tree[++sz]=tree[now]; now=sz;
tree[now].w++;
if (l==r) return;
int mid=(l+r)>>1;
if (x<=mid) insert(tree[now].l,l,mid,x);
else insert(tree[now].r,mid+1,r,x);
}
int qurry(int i,int j,int l,int r,int k)
{
if (l==r) return l;
int t=tree[tree[j].l].w-tree[tree[i].l].w;
int mid=(l+r)>>1;
if (t>=k) return qurry(tree[i].l,tree[j].l,l,mid,k);
else return qurry(tree[i].r,tree[j].r,mid+1,r,k-t);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[++s]=a[i];
sort(b+1,b+s+1);
s=unique(b+1,b+s+1)-b-1;
for (int i=1;i<=n;i++) p[i]=lower_bound(b+1,b+s+1,a[i])-b;
sz=0; root[0]=0;
for (int i=1;i<=n;i++)
{
root[i]=root[i-1];
insert(root[i],1,n,p[i]);
}
while (m--)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int t=qurry(root[x-1],root[y],1,n,z);
printf("%d\n",b[t]);
}
}
普及向:
喵喵喵竟然发现自己还不知道什么是可持久化。。。
可持久数据结构主要指的是我们可以查询历史版本的情况并支持插入,利用使用之前历史版本的数据结构来减少对空间的消耗(能够对历史进行修改的是函数式)。
一般的实现方法是新建有修改的点 其他点与上一版本共用 这样做到空间复杂度带上一个或两个log
比较常用的—–可持久化线段树(主席树)+可持久化trie树
然后喵喵喵就发现自己对于主席树的程度只有默写板子,任重而道远!
主席树(可持久化线段树,函数式线段树)从区间第k大的问题入手,这可是裸题呀,推荐一波优秀的up?
时间复杂度:构造O(nlogn),单次查询O(logn)
那么重点就是,每个节点建一棵区间[1,n]的线段树,每个节点存储的是在该区间内出现的数字的个数,这里的每一棵线段树的区间就是数字大小的区间
我还是举个栗子吧
我们可以发现每一步到下一步的时候大部分的节点都是一样的,我们就把ta直接拉过来,实现可持久化,最优秀的拉拢方法是:单独修改要修改的路,被修改的肯定是新编号的节点,除了这条路径上的承接自父亲,父亲承接自上一个根节点
至于扩展到一个区间[x,y]就[1,x],[1,y]对应的两个圈内的数字相减就好了
想要查询区间第k大的时候,用类似splay的find操作就行,还是灰常简单的