静态区间第k大数

划分树版本:

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#define MAXN 100002
#define MLOG 19
using namespace std;
int tree[MLOG][MAXN],cntleft[MLOG][MAXN],sorted[MAXN];
void buildtree(int lef,int rig,int deep)
{
	if(lef==rig)return;
	int mid=(lef+rig)>>1,same=0,lpos=lef,rpos=mid+1;
	for(int i=lef;i<=rig;i++)
		if(tree[deep][i]<sorted[mid])same++;
	same=mid-lef+1-same;
	for(int i=lef;i<=rig;i++)
	{
		if(tree[deep][i]<sorted[mid]||
				(tree[deep][i]==sorted[mid]&&(same--)>0))
		{
			tree[deep+1][lpos++]=tree[deep][i];
			cntleft[deep][i]=cntleft[deep][i-1]+1;
		}
		else
		{
			tree[deep+1][rpos++]=tree[deep][i];
			cntleft[deep][i]=cntleft[deep][i-1];
		}
	}
	buildtree(lef,mid,deep+1);
	buildtree(mid+1,rig,deep+1);
}
int query(int lef,int rig,int qlef,int qrig,int deep,int k)
{//区间总长l,r,查询ql至qr到第k小数
	while(lef<rig)
	{
		int mid=(lef+rig)>>1,newl,newr;
		int temp=cntleft[deep][qrig]-cntleft[deep][qlef-1];
		if(temp>=k)
		{
			newl=cntleft[deep][qlef-1]-cntleft[deep][lef-1]+lef;
			newr=newl+temp-1;
			return query(lef,mid,newl,newr,deep+1,k);
		}
		else
		{
			newr=qrig+cntleft[deep][rig]-cntleft[deep][qrig];
			newl=newr-(qrig-qlef-temp);
				return query(mid+1,rig,newl,newr,deep+1,k-temp);
		}
	}
	return tree[deep][qlef];
}
int nextint()
{
	bool flag=0;
	char ch;
	while(ch=getchar())
	{
		if(ch=='-')
			flag=1;
		if(ch>='0'&&ch<='9')
			break;
	}
	int ans=ch-'0';
	while(ch=getchar())
	{
		if(ch<'0'||ch>'9')break;
		ans*=10,ans+=ch-'0';
	}
	if(flag==1)
		ans=-ans;
	return ans;
}
//注意初始化cntleft,排序sorted,buildtree(1,n,0)
int main()
{
	int cas;
	cas=1;
	while(cas--)
	{
		memset(cntleft,0,sizeof(cntleft));
		int n,m;
		n=nextint();
		m=nextint();
		for(int i=1;i<=n;i++)
		{
			sorted[i]=nextint();
			tree[0][i]=sorted[i];
		}
		sort(sorted+1,sorted+n+1);
		buildtree(1,n,0);
		int a,b,c;
		for(int i=0;i<m;i++)
		{
			a=nextint();
			b=nextint();
			c=nextint();
			printf("%d\n",query(1,n,a,b,0,c));
		}
	}
	return 0;
}

归并树版本

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N=100010;
const int DEEP=20;

int seq[DEEP][N]; //归并树
int a[N], n, m;
struct node
{
	int l, r, mid;
} tree[N*4]; //线段树

//线段树+归并树一起建了,实际上二者的过程一样。。
void build(int l, int r, int p, int deep)
{
	tree[p].l = l;
	tree[p].r = r;
	tree[p].mid = (l+r)>>1;
	if(l==r)
	{
		seq[deep][l] = a[l];
		return;
	}
	build(l, tree[p].mid, p*2, deep+1);
	build(tree[p].mid+1, r, p*2+1, deep+1);

	//归并过程
	int i, j, k;
	for(i=l, j=tree[p].mid+1, k=l; i<=tree[p].mid && j<=r; )
	{
		if(seq[deep+1][i]>seq[deep+1][j])
			seq[deep][k++] = seq[deep+1][j++];
		else
			seq[deep][k++] = seq[deep+1][i++];
	}
	while(i<=tree[p].mid)
		seq[deep][k++] = seq[deep+1][i++];
	while(j<=r)
		seq[deep][k++] = seq[deep+1][j++];
}

//通过二分枚举,返回key在本区间大于多少个数
//注意:任何一个通过线段树最终到达的区间一定是已经排好序了的,所以可以通过二分求
int counthelp(int l, int r, int p, int key, int deep)
{
	int mid;
	while(l<=r)
	{
		mid = (l+r)>>1;
		if(seq[deep][mid]<key)
			l = mid+1;
		else
			r = mid-1;
	}
	return r-tree[p].l+1;
}
//返回key在[l, r]总区间内大于几个数
int count(int l, int r, int p, int key, int deep)
{
	if(tree[p].l==l && tree[p].r==r)
	{
		return counthelp(l, r, p, key, deep);
	}
	if(r<=tree[p].mid)
		return count(l, r, p*2, key, deep+1);
	else if(l>tree[p].mid)
		return count(l, r, p*2+1, key, deep+1);
	else
	{
		return count(l, tree[p].mid, p*2, key, deep+1)+count(tree[p].mid+1, r, p*2+1, key, deep+1);
	}
}

//返回在通过二分枚举后得到的结果。。。
int query(int ll, int rr, int cnt)
{
	int mid, tmp;
	int l = 1;
	int r = n;
	while(l<=r)
	{
		mid = (l+r)>>1;
		tmp = count(ll, rr, 1, seq[1][mid], 1); 
		if(tmp>=cnt)
			r = mid-1;
		else
			l = mid+1;
	}
	return seq[1][l-1];
}

int main()
{
	int i, x, y, cnt;
	int t;
	cin >> t;
	while(t--)
	{
		scanf("%d %d", &n, &m);
		for(i=1; i<=n; i++)
			scanf("%d", &a[i]);
		build(1, n, 1, 1);
		while(m--)
		{
			scanf("%d%d%d", &x, &y, &cnt);
			printf("%d\n", query(x, y, cnt));
		}
	}
	return 0;
}

块链神马的以后填坑。。。

可持久化线段树什么的以后填坑。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值