主席树(不带修改)小结

听这名字如此霸气,学之前感觉很慌。
网上许多教程真的是讲不清楚(估计是我蠢)ε=(´ο`*)))唉。
推荐:
讲解的好的:(https://www.cnblogs.com/Empress/p/4652449.html)
代码较好的:(http://blog.csdn.net/creatorx/article/details/75446472)
水啊。。。。%%%%%o(╥﹏╥)o
这里写图片描述


正题

主席树,又叫函数式线段树,不知道这个主席是怎么取来的,据说。。。。。
其实就是遵循着“只添加,不修改”的原则,保留了未修改时的信息,所有可以持久化。。。。然而还是不会带修改的。。。

主席树可以解决区间第k大(小)问题,不带修改是基础,带修改就要加个(树状数组)。

题目

给一段区间,求第k小值。

思路

每个一次对一个区间排序,求第k小值。。。。。主席树(不带修改)小结_第1张图片

好吧,就是主席树好了。。

解法

首先求第k小我们可以用线段树。
举个例子(用了上面那个大佬的例子):
1 2 5 1 3 2 2 5 1 2
主席树(不带修改)小结_第2张图片
这课权值线段树里叶子节点表示该数的数量。非叶子节点(除了根)表示数量和。
比如说我们要求第5小的值,先从根看,左边有8个,5 < 8,说明第五小的值在左边。
下一层后,左边有7个,5 < 7,还在左边。
下一层,左边有3 个,5>3,说明第五小的在右边,我们现在需要找右边第(5-3)小的就行了。
最终找到了是2。

建树的复杂度是O(nlogN),查询的复杂度是O(logN) (这里的N是不相同数的数量)
若我们仍对每个查询建树,那么复杂度丝毫没有降低(反而提高了)。

采用前缀和思想,若求区间[l,r],用[1,r]-[1,l-1]即可。
这就相当于建了[1,1],[1,2]……[1,n]共n棵线段树了。
上述例子,比如求[5,10]的第k小值。
分别建出[1,4],[1,10]的,相减即可。
主席树(不带修改)小结_第3张图片主席树(不带修改)小结_第4张图片主席树(不带修改)小结_第5张图片
再做上面查询的操作。

然而:建n棵线段树……..是痴人说梦的吧……..空间不炸才怪。
对此:我们可以发现:[1,n-1]与[1,n]的线段树区别,只是在于第a[n]个数而已,我们可以用上前面n-1的信息,再加上这第n个数就好了,新加上不超过log(n)个节点。就类似于这样。
主席树(不带修改)小结_第6张图片
这东西要在代码里面自己看怎么打比较好。

hdu2665,poj2104,2761

求区间第k小

#include
#include
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=1e5+5;
struct cy{
    int val,id;
}a[maxn];
struct tre{
    int l,r;
    int sum;
}tree[maxn*20];
int rank[maxn],root[maxn],n,m,cnt;

bool cmp(cy a,cy b)
{
    return a.valvoid add(int &now,int l,int r,int x)
{
    tree[++cnt]=tree[now];
    now=cnt;
    tree[now].sum++;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (x<=mid) add(tree[now].l,l,mid,x);
    else add(tree[now].r,mid+1,r,x);
}
int getans(int L,int R,int sum,int l,int r)
{
    int k=tree[tree[R].l].sum-tree[tree[L].l].sum;
    if (l==r) return l;
    int mid=(l+r)>>1;
    if (sum<=k) return getans(tree[L].l,tree[R].l,sum,l,mid);
    else return getans(tree[L].r,tree[R].r,sum-k,mid+1,r);
}
int main()
{
    freopen("T.in","r",stdin);
//  freopen("T.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i].val),a[i].id=i;
    sort(a+1,a+n+1,cmp);
    fo(i,1,n) rank[a[i].id]=i;
    fo(i,1,n){
        root[i]=root[i-1];
        add(root[i],1,n,rank[i]);
    }
    int l,r,w,ans;
    fo(i,1,m){
        scanf("%d%d%d",&l,&r,&w);
        ans=getans(root[l-1],root[r],w,1,n);
        printf("%d\n",a[ans].val);
    }
}

主席树(不带修改)小结_第7张图片

你可能感兴趣的:(信息技术,线段树,区间问题,主席树)