树状数组也求第k小的数
(
不会?
)
虽然码量小,速度快,但有缺点。因为它建的是权值线段树,所以如果权值太大,它就无法正常运作。
有一种方法可以解决零散的大数,那就是离散化。本篇文章就来介绍一下用离散化优化其空间的树状数组。
了很多砖头,而且把他们叠成很多个柱子,因为他法力无边,劈一个砖头实在是太无趣了。但是破坏王欧老师又上线了,
1.从某柱砖的顶端拿一块砖出来,丢向欧老师.
2.从欧老师手中抢来一块砖,放到任意一柱.欧老师有的砖无限多.
有一种方法可以解决零散的大数,那就是离散化。本篇文章就来介绍一下用离散化优化其空间的树状数组。
思路
把所有数离散化,该离散化只需要能保留数字排名的前后即可,对区间信息等不作要求。
离散之后,按照离散后的权值插入树状数组,用这个树状数组就可以求出第k小的数的离散值了,实际值再转换一下就好了。
在离散化后,原本很大的权值就能被压缩,因为树状数组是按权值建的,于是它的大小就能大大减小。加上离散化的树状数组求第k大,能更好的适应更大的数据。大佬题面:
为了大家在机房不太无聊,不知道是谁弄来了点砖头,可能是为了让大家平时练一练空手劈砖头?AKC老师很开心地抢到了很多砖头,而且把他们叠成很多个柱子,因为他法力无边,劈一个砖头实在是太无趣了。但是破坏王欧老师又上线了,
不过他这次是把C老师的砖头柱弄得参差不齐,处女座的C老师希望在这N柱砖里面有连续的K柱的高度是一样的。
你可以选择以下两个动作1.从某柱砖的顶端拿一块砖出来,丢向欧老师.
2.从欧老师手中抢来一块砖,放到任意一柱.欧老师有的砖无限多.
现在希望用最小次数的动作完成任务.
简易题面:
给一个长为N的序列,找出其中一段长为K的子序列,使其中各个数与中位数的差的总和最小,求这个总和。正解
树状数组要求中位数,其实也可以转换成求第k小的数,所以树状数组直接上。
枚举所有的符合大小的区间,找到中位数后,再开一个树状数组来求区间和。判断其是否为最优解即可。
为了能使树状数组的求知等更加方便,减小树状数组的空间,我们给数据进行离散。代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
int bin[30];
int n;
int h[maxn],ys[maxn],yss[maxn];//ys[真值]=离散值, yss[离散值]=真值
long long s1[maxn],s2[maxn];//s1用于记录数字的个数,s2用于求一段权值区间内的权值和
int lowbit(int x)
{
return x&-x;
}
void change(long long s[],int x,int c)
{
for(;x<=n;x+=lowbit(x))
{
s[x]+=c;
}
}
long long getsum(long long s[],int x)
{
long long sum=0;
for(;x>=1;x-=lowbit(x))
{
sum+=s[x];
}
return sum;
}
int erfen(int x)
{
int l=1,r=n,ans;
while(l<=r)
{
int mid=(l+r)/2;
if(yss[mid]<=x)
{
l=mid+1;
ans=mid;
}
else
{
r=mid-1;
}
}
return ans;
}
int query(int k)
{
int now=0,sum=0;//now是中位数的数值,sum统计在now左边出现了几个数
for(int i=20;i>=0;i--)
{
if(now+bin[i]<=n && sum+s1[now+bin[i]]<k)
{
now+=bin[i];
sum+=s1[now];
}
}
return now+1;
}
int main()
{
bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]*2;
int k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
yss[i]=h[i];
}
sort(yss+1,yss+n+1);
for(int i=1;i<=n;i++) ys[i]=erfen(h[i]);
for(int i=1;i<=n;i++) yss[ys[i]]=h[i];
for(int i=1;i<k;i++)
{
change(s1,ys[i],1);
change(s2,ys[i],h[i]);//个数统计和区间求值都按离散权值插入
}
long long ans=((long long)1<<62)-1;
for(int i=k;i<=n;i++)
{
change(s1,ys[i],1);
if(i!=k) change(s1,ys[i-k],-1);
change(s2,ys[i],h[i]);
if(i!=k) change(s2,ys[i-k],-h[i-k]);
int mid=query((k+1)/2);
long long hs=0;
hs+=getsum(s1,mid-1)*yss[mid]-getsum(s2,mid-1);
hs+=(getsum(s2,n)-getsum(s2,mid))-(long long)(getsum(s1,n)-getsum(s1,mid))*yss[mid];
if(hs<ans) ans=hs;
}
printf("%lld\n",ans);
return 0;
}

本文介绍如何通过离散化方法优化树状数组的空间占用,实现求解第k小的数的问题。适用于权值较大的场景,通过离散化压缩权值范围,使树状数组能更高效地处理大数据量。

1858

被折叠的 条评论
为什么被折叠?



