[POJ2104]K-th Number(静态主席树)

来源:互联网 发布:json乱码转换成中文 编辑:程序博客网 时间:2024/06/04 19:35

题目描述

传送门

题解

静态主席树维护。

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int max_n=1e5+5;int val[max_n],loc[max_n],num[max_n],root[max_n];struct hp{    int l,r,val;}ptree[max_n*20];int n,m,x,y,z,sz,ans;inline int cmp(int a,int b){    return val[a]<val[b];}inline void build(int &now,int l,int r,int x){    int mid=(l+r)>>1;    ptree[++sz]=ptree[now]; now=sz;    ptree[now].val++;    if (l==r) return;    if (x<=mid)      build(ptree[now].l,l,mid,x);    else      build(ptree[now].r,mid+1,r,x);}inline int query(int i,int j,int l,int r,int k){    if (l==r) return l;    int mid=(l+r)>>1;    int t=ptree[ptree[j].l].val-ptree[ptree[i].l].val;    if (t>=k) return query(ptree[i].l,ptree[j].l,l,mid,k);    else return query(ptree[i].r,ptree[j].r,mid+1,r,k-t);}int main(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;++i){        scanf("%d",&val[i]);        loc[i]=i;    }    sort(loc+1,loc+n+1,cmp);    for (int i=1;i<=n;++i)      num[loc[i]]=i;    sz=0; root[0]=0;    for (int i=1;i<=n;++i){        root[i]=root[i-1];        build(root[i],1,n,num[i]);    }    for (int i=1;i<=m;++i){        scanf("%d%d%d",&x,&y,&z);        ans=query(root[x-1],root[y],1,n,z);        printf("%d\n",val[loc[ans]]);    }}

总结

抄别人的模板,但是还是想了很久很久,旁边的hxy神犇(一眼秒懂)和ATP神犇(一眼秒懂)看着我说不出话来,╮(╯▽╰)╭没办法只能怪自己太煞笔。
①普通的线段树是不可减的,但是我们分别维护1-2,1-3,1-4……1-n的权值线段树就可以转化成相减的线段树(权值线段树的意思就是排序后按照下标维护,也就是说将数据离散化之后原始数据变得毫无意义,我们需要的是这个数据在原序列中位于第几小的位置,以此来建树)。
②由于维护1~i+1这个区间的线段树时只比1~i这个区间的线段树多出来了一个数,所以只会在前一个线段树上影响一条链的值(从根到叶子)。
③每一次修改只是存一条链,即空间复杂度为O(nlogn),在前一个的基础上进行修改,并且设指针记录。
④由于从1~n棵树的形态都是相同的(空结点也算),可以通过一层一层的指针找出这棵树的完整形态。
⑤给初学者的忠告:这真的是非常简单易懂的代码模板,如果不懂的话就调试一下,注意递归调用时&的作用。当把watch的所有数据列出来之后,原理一目了然。

0 0