有 N 个位置,M 个操作。每个位置可以同时存储多个数。

操作有两种,每次操作:

如果是 1 a b c 的形式,表示在第 a 个位置到第 b 个位置,每个位置加入一个数 c。
如果是 2 a b c 的形式,表示询问从第 a 个位置到第 b 个位置,第 c 大的数是多少。
输入格式
第一行包含两个整数 N,M。

接下来 M 行,每行包含一条指令,形如 1 a b c 或 2 a b c。

输出格式
输出每个询问的结果,每个结果占一行。

数据范围
1≤N,M≤50000,
1≤a≤b≤N,
1 操作中的 c 的绝对值不超过 N,
2 操作中 c 满足 1≤c≤231−1。

思路:
开权值线段树 ,权值线段树上的每个点对应题目所给的位置,然后就是区间修改和区间查询了,然后如果传统全开线段树的话 空间N^2,这里我们用动态开点,空间优化到NlogNlogN

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=50010,P=N*16*16,M=4*N;
struct node{
   
	int l;
	int r;
	int add;
	int sum;
}tr[P];
int n,m;
int T[M],L[M],R[M];
struct qr{
   
	int op,a,b,c;
}q[N];
int idx;
vector<int>nums;
int get(int x)
{
   
	return lower_bound(nums.begin(),nums.end(),x)-nums.begin();
}
int intersection(int a,int b,int c,int d)
{
   
	return min(b,d)-max(a,c)+1;
}
void build(int u,int l,int r)
{
   
	T[u]=++idx,L[u]=l,R[u]=r;
	if(l==r)	return ;
	int mid=l+r>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}


void update(int u,int l,int r,int pl,int pr)
{
   
	tr[u].sum+=intersection(l,r,pl,pr);
	if(l>=pl && r<=pr)
	{
   
		tr[u].add++;
		return ;
	}
	int mid=l+r>>1;
	if(pl<=mid)
	{
   
		if(!tr[u].l) tr[u].l=++idx;
		update(tr[u].l,l,mid,pl,pr);
	}
	if(pr>mid)
	{
   
		if(!tr[u].r) tr[u].r=++idx;
		update(tr[u].r,mid+1,r,pl,pr);
	}

}
void change(int u,int a,int b,int c)
{
   
	update(T[u],1,n,a,b);
	if(L[u]==R[u])
		return ;
	int mid=L[u]+R[u]>>1;
	if(c<=mid)	change(u<<1,a,b,c);
	else	change(u<<1|1,a,b,c);
}
int get_sum(int u,int l,int r,int pl,int pr,int add)
{
   
	if(l>=pl && r<=pr)
	{
   
		return tr[u].sum+(r-l+1)*add;
	}
	add+=tr[u].add;
	int res=0;
	int mid=l+r>>1;
	if(pl<=mid)
	{
   
		if(tr[u].l)
			res+= get_sum(tr[u].l,l,mid,pl,pr,add);
		else
			res+=add*intersection(l,mid,pl,pr);
	}


	if(pr>mid)
	{
   
		if(tr[u].r)
			res+=get_sum(tr[u].r,mid+1,r,pl,pr,add);
		else
			res+=add*intersection(mid+1,r,pl,pr);
	}
	return res;

}
int query(int u,int a,int b,int k)
{
   
	if(L[u]==R[u])
		return R[u];
	int c=get_sum(T[u<<1|1],1,n,a,b,0);
	if(k<=c)
		return query(u<<1|1,a,b,k);
	else
		return query(u<<1,a,b,k-c);
}
int main()
{
   
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
   
		int op,a,b,c;
		cin>>op>>a>>b>>c;
		q[i]={
   op,a,b,c};
		if(op==1)
			nums.push_back(c);
	}
	sort(nums.begin(),nums.end());
	nums.erase(unique(nums.begin(),nums.end()),nums.end());
	build(1,0,nums.size()-1);
	for(int i=0;i<m;i++)
	{
   
		int op=q[i].op,a=q[i].a,b=q[i].b,c=q[i].c;
		if(op==1)
		{
   
			change(1,a,b,get(c));
		}
		else
		{
   
			cout<<nums[query(1,a,b,c)]<<endl;
		}
	}
	return 0;
}
// 2 5
// 1 1 2 1
// 1 1 2 2
// 2 1 1 2
// 2 1 1 1
// 2 1 2 3