线段树大概地球人都知道了,就是以数组的下表建立线段树来进行一些区间操作,这里介绍一下权值线段树,顾名思义,其实权值线段树也是线段树的一种。
一:权值线段树线段树与简单线段树的区别就像他的名字一样,他的叶子节点存的并不是数组的下表,而是数组中数的权值,这种操作很简单的解决一些问题。
二.例题分析
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<
题目描述:你知道有一个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
缺点:在数据很大的时候,空间可能会爆,需要离线化处理