树状数组—求第k小的数—离散化

树状数组也求第k小的数 不会? 虽然码量小,速度快,但有缺点。因为它建的是权值线段树,所以如果权值太大,它就无法正常运作。
有一种方法可以解决零散的大数,那就是离散化。本篇文章就来介绍一下用离散化优化其空间的树状数组。


思路

把所有数离散化,该离散化只需要能保留数字排名的前后即可,对区间信息等不作要求。

离散之后,按照离散后的权值插入树状数组,用这个树状数组就可以求出第k小的数的离散值了,实际值再转换一下就好了。

在离散化后,原本很大的权值就能被压缩,因为树状数组是按权值建的,于是它的大小就能大大减小。加上离散化的树状数组求第k大,能更好的适应更大的数据。


例题

大佬题面:

为了大家在机房不太无聊,不知道是谁弄来了点砖头,可能是为了让大家平时练一练空手劈砖头?AKC老师很开心地抢到
了很多砖头,而且把他们叠成很多个柱子,因为他法力无边,劈一个砖头实在是太无趣了。但是破坏王欧老师又上线了,

不过他这次是把C老师的砖头柱弄得参差不齐,处女座的C老师希望在这N柱砖里面有连续的K柱的高度是一样的。

你可以选择以下两个动作
1.从某柱砖的顶端拿一块砖出来,丢向欧老师.
2.从欧老师手中抢来一块砖,放到任意一柱.欧老师有的砖无限多.

现在希望用最小次数的动作完成任务.

简易题面:

给一个长为N的序列,找出其中一段长为K的子序列,使其中各个数与中位数的差的总和最小,求这个总和。


正解

树状数组

要求中位数,其实也可以转换成求第k小的数,所以树状数组直接上。

枚举所有的符合大小的区间,找到中位数后,再开一个树状数组来求区间和。判断其是否为最优解即可。

为了能使树状数组的求知等更加方便,减小树状数组的空间,我们给数据进行离散。


代码

#include
#include
#include
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]]


你可能感兴趣的:(树状数组,基础数据的超进化)