权值线段树小结

线段树大概地球人都知道了,就是以数组的下表建立线段树来进行一些区间操作,这里介绍一下权值线段树,顾名思义,其实权值线段树也是线段树的一种。

一:权值线段树线段树与简单线段树的区别就像他的名字一样,他的叶子节点存的并不是数组的下表,而是数组中数的权值,这种操作很简单的解决一些问题。


二.例题分析

1.求逆序对数(hdu 1394)

好吧,其实这种题直接用归并就可以解决吗,但是我们呢偏要任性用权值线段树解决

每次插入一个数,然后统计他前边的数比他的个数,例如当我们插入的数是x,那么我们找x+1~n里边已经存在的数的个数就可以了


ac代码:

#include 
#include 
#include 
using namespace std;
const int maxn=5005;
struct node
{
    int l,r,num;
}data[4*maxn];
void build(int id,int l,int r)
{
    data[id].l=l;
    data[id].r=r;
    data[id].num=0;
    if(l==r)
        return ;
    int mid=(l+r)/2;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
}
int query(int id,int l,int r)
{
    if(data[id].l==l&data[id].r==r)
        return data[id].num;
    int mid=(data[id].l+data[id].r)/2;
    if(mid>=r)
        return query(id*2,l,r);
    else if(mid=x)
        update(id*2,x);
    else
        update(id*2+1,x);
    data[id].num=data[id*2].num+data[id*2+1].num;
    return ;
}
int a[maxn];
int main()
{
    int n;
    while(cin>>n)
    {
        build(1,1,n+1);
        int ans=0,x;
        for(int i=0;i=0;i--)
        {
            ans=ans-(n-a[i])+(a[i]-1);
            sum=min(sum,ans);
        }
        cout<

2.根据逆序对数还原原来数列(hdu 5592)

题目描述:你知道有一个1~n的排列,但具体排列你不知道。现在给出1~n每个前缀的逆序数对数,让你还原这个排列

把第一个问题反过来了,知道逆序数让我们求解序列,我们从后往前看,设前缀数组为p那么对于第i个数来说sum=p[i]-p[i-1]表示的恰好就是他的前边比他大的数的个数,所以该位置的数字就是剩下的这些数字里的第i-sum大的数字。于是我们把1~n构建一棵权值线段树。初始化每个数字的权值都是1。然后从n~1处理。每一次都query取出第i-sum大的数字作为当前位置的答案,然后update将该数字从该权值线段树删除,然后到前一个位置继续相同操作。最后就能得出这样一个正确的排列了。

ac代码:

#include
#include
#include
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
struct segtree
{
    int l,r,val,num;
}tree[400010];
int n,m,rev[50010],ans[50010];
void init(int i,int l,int r)
{
    tree[i].l=l;
    tree[i].r=r;
    tree[i].num=r-l+1;
    if(l==r)
    {
        tree[i].val=l;
        return ;
    }
    int mid=(l+r)>>1;
    init(i<<1,l,mid);
    init((i<<1)|1,mid+1,r);
}
int query(int i,int k)
{
    if(tree[i].l==tree[i].r)
        return tree[i].val;
    if(tree[i<<1].num>=k)
        return query(i<<1,k);
    else
        return query((i<<1)|1,k-tree[i<<1].num);
}
void update(int i,int pos)
{
    tree[i].num--;
    if(tree[i].l==tree[i].r)
        return ;
    if(pos<=tree[i<<1].r)
        update(i<<1,pos);
    else
        update((i<<1)|1,pos);
    return ;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(rev,0,sizeof(rev));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)
        scanf("%d",&rev[i]);
        init(1,1,n);
        for(int i=n;i>=1;i--)
        {
            ans[i]=query(1,i-(rev[i]-rev[i-1]));
            update(1,ans[i]);
        }
        for(int i=1;i

缺点:在数据很大的时候,空间可能会爆,需要离线化处理



你可能感兴趣的:(线段树)