K大数查询(线段树套线段树)

Description

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

Input

第一行N,M
接下来M行,每行形如1 a b c或2 a b c

Output

输出每个询问的结果

Sample Input

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

Sample Output

1
2
1

Hint

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1

的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是

1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3

大的数是 1 。‍

N,M<=50000,N,M<=50000

a<=b<=N

1操作中abs©<=N

2操作中c<=Maxlongint

思路

本题的第k大数, 仔细读题可以发现我们可以采取二分的方法, 二分可能的第k大的数据, 对于二分时的mid, 我们就查询对于mid所建的线段树中b~e区间中的数有多少(因为在插入时只有当有大于等于mid的值时才会更新这个点所以这个点mid线段树中的值都是大于mid的)
细节见于代码

#include <cstdio>
#include <algorithm>
#define N 1000010
#define M 20000010
using namespace std;
int n , root[N] , ls[M] , rs[M] , tot;
unsigned sum[M] , tag[M];
void pushdown(int l , int r , int x)
{
	if(tag[x])
	{
		int mid = (l + r) >> 1;
		if(!ls[x]) ls[x] = ++tot;
		if(!rs[x]) rs[x] = ++tot;
		sum[ls[x]] += (mid - l + 1) * tag[x] , tag[ls[x]] += tag[x];
		sum[rs[x]] += (r - mid) * tag[x] , tag[rs[x]] += tag[x];
		tag[x] = 0;
	}
}
void update(int b , int e , int l , int r , int &x)
{
	if(!x) x = ++tot;
	if(b <= l && r <= e)
	{
		sum[x] += (r - l + 1) , tag[x] ++ ;
		return;
	}
	pushdown(l , r , x);
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , l , mid , ls[x]);
	if(e > mid) update(b , e , mid + 1 , r , rs[x]);
	sum[x] = sum[ls[x]] + sum[rs[x]];
}
unsigned query(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e) return sum[x];
	pushdown(l , r , x);
	int mid = (l + r) >> 1;
	unsigned ans = 0;
	if(b <= mid) ans += query(b , e , l , mid , ls[x]);
	if(e > mid) ans += query(b , e , mid + 1 , r , rs[x]);
	return ans;
}
void modify(int b , int e , int p , int l , int r , int x)
{
	update(b , e , 1 , n , root[x]);
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) modify(b , e , p , l , mid , x << 1);
	else modify(b , e , p , mid + 1 , r , x << 1 | 1);
}
int solve(int b , int e , unsigned a , int l , int r , int x)
{
	if(l == r) return l;
	int mid = (l + r) >> 1;
	unsigned tmp = query(b , e , 1 , n , root[x << 1 | 1]);
	if(tmp >= a) return solve(b , e , a , mid + 1 , r , x << 1 | 1);
	else return solve(b , e , a - tmp , l , mid , x << 1);
}
int main()
{
	int m , opt , x , y , z;
	unsigned t;
	scanf("%d%d" , &n , &m);
	while(m -- )
	{
		scanf("%d%d%d" , &opt , &x , &y);
		if(opt == 1) scanf("%d" , &z) , modify(x , y , z + n + 1 , 1 , 2 * n + 1 , 1);
		else scanf("%u" , &t) , printf("%d\n" , solve(x , y , t , 1 , 2 * n + 1 , 1) - n - 1);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值