权值线段树小结

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

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


二.例题分析

1.求逆序对数(hdu 1394)

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

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


ac代码:

#include <iostream>
#include <cstdio>
#include <cstring>
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<l)
        return query(id*2+1,l,r);
    else
        return query(id*2,l,mid)+query(id*2+1,mid+1,r);
}
void update(int id,int x)
{
    if(data[id].l==x&&data[id].r==x)
    {
        data[id].num++;
        return ;
    }
    int mid=(data[id].l+data[id].r)/2;
    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<n;i++)
        {
            scanf("%d",&a[i]);
            a[i]++;
            ans+=query(1,a[i]+1,n+1);
            update(1,a[i]);
        }
        int sum=ans;
        for(int i=n-1;i>=0;i--)
        {
            ans=ans-(n-a[i])+(a[i]-1);
            sum=min(sum,ans);
        }
        cout<<sum<<endl;
    }
    return 0;
}

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<cstdio>
#include<iostream>
#include<cstring>
#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<n;i++)
            printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
    }
    return 0;
}

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



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值