[BZOJ3110]K大数查询|线段树套线段树

本文介绍了一种使用线段树解决复杂查询问题的方法——线段树套线段树,并提供了两种实现思路:外层权值线段树内层区间线段树和外层区间内层权值线段树。通过具体代码详细解析了每一步操作,包括更新、插入和查询等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

妈呀这题好神好神好神。。我发现主席树好像做不了呀。。咋全写的是线段树套线段树呢。。后来还是看黄学长的代码看懂了。。果然我是看黄学长博客长大的。。有两种做法,大部分人是外层权值线段树,内层区间线段树,这个我写了,还是很好写。。lyd给了一种外层区间内层权值的做法,没看懂。。内层区间[l1,r1]在外层权值区间[l,r]下的意义是在[l1r1]内有多少权值在[l,r]之间的数。。由于一开始是空的,每次最多访问nlog^2 n个节点,所以我们用实时开点,空间复杂度Onlog^2 n)。。具体的还是好好看代码。。

#include<iostream>
#include<cstdio>
#define N 200005
using namespace std;
int n,m,opt,a,b,c,i,nd=0,root[N],lc[100*N],rc[100*N],sum[100*N],tag[100*N];
void mark(int &x,int l,int r,int c)
{
	if (!x) x=++nd,lc[x]=rc[x]=tag[x]=sum[x]=0;
	tag[x]+=c;sum[x]+=c*(r-l+1);
}
void down(int x,int l,int r)
{
	if (!tag[x]||l==r) return;
	mark(lc[x],l,(l+r)>>1,tag[x]);mark(rc[x],((l+r)>>1)+1,r,tag[x]);
	tag[x]=0;
}
void update(int &x,int l,int r,int a,int b)
{
	if (!x) x=++nd,lc[x]=rc[x]=tag[x]=sum[x]=0;
	down(x,l,r);
	if (l==a&&r==b)
	{
		tag[x]++;sum[x]+=b-a+1;
		return;
	}
	int mid=(l+r)>>1;
	if (b<=mid) update(lc[x],l,mid,a,b);
	else if (a>mid) update(rc[x],mid+1,r,a,b);
	else update(lc[x],l,mid,a,mid),update(rc[x],mid+1,r,mid+1,b);
	sum[x]=sum[lc[x]]+sum[rc[x]];
}
void insert(int a,int b,int c)
{
	int now=1,l=1,r=n,mid;
	while (l<r)
	{
		update(root[now],1,n,a,b);
		mid=(l+r)>>1;now<<=1;
		if (c<=mid) r=mid; else l=mid+1,now+=1;
	}
	update(root[now],1,n,a,b);
}
int query(int x,int l,int r,int a,int b)
{
	if (!x) return 0;
	down(x,l,r);
	if (l==a&&r==b) return sum[x];
	int mid=(l+r)>>1;
	if (b<=mid) return query(lc[x],l,mid,a,b);
	else if (a>mid) return query(rc[x],mid+1,r,a,b);
	else return query(lc[x],l,mid,a,mid)+query(rc[x],mid+1,r,mid+1,b);
}
int solve(int a,int b,int k)
{
	int now=1,l=1,r=n,mid,s;
	while (l<r)
	{
		int mid=(l+r)>>1;now<<=1;
		s=query(root[now],1,n,a,b);
		if (s>=k) r=mid;else l=mid+1,now++,k-=s;
	}
	return l;
}
int main()
{
	freopen("3110.in","r",stdin);
	freopen("3110.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (i=1;i<=N-1;i++) root[i]=0;
	while (m--)
	{
		scanf("%d%d%d%d",&opt,&a,&b,&c);
		if (opt==1) insert(a,b,n-c+1);
		else printf("%d\n",n-solve(a,b,c)+1);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值