传送门
找第
K
K
K大的时候把大于
m
i
d
mid
mid的标记为1.
如果把大于等于
m
i
d
mid
mid的标为1,那么有可能第
K
K
K大就是
m
i
d
mid
mid而当前统计的
1
1
1的个数大于
K
K
K(即有多个数为
m
i
d
mid
mid),于是答案会被往上调(为了减少
1
1
1的个数)。
也就是说答案范围变为
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r]。本来答案是
m
i
d
mid
mid。就出锅了。
比如说:3,4,4,4,5 求1~5中的第三大。(显然是4)
假设当前
m
i
d
mid
mid为4,于是数列变成0,1,1,1,1。
由于
4
>
3
4>3
4>3,那么二分的答案要上调,要把答案锁定在
[
m
i
d
+
1
,
5
]
[mid+1,5]
[mid+1,5]
于是得到了错误答案。
所以要把大于 m i d mid mid的标为1。
具体做法是:把操作按照答案范围分为两部分。
修改操作就按照修改值与
m
i
d
mid
mid大小比较,因为对应修改值只会在对应答案范围内产生贡献。
询问 k k k大就按照当前的操作顺序一步一步操作,然后遇见询问操作就统计区间和。和大了就要把答案上调。和小了就先减去这部分和(比它大的数的个数),然后把答案调小。
然后做完之后要把影响消回来,就是代码中区间减的部分。
50000次给50000个位置加上一个数。于是线段树统计sum爆int
输入有负数,注意读优
#include<bits/stdc++.h>
#define lc (root<<1)
#define rc (root<<1|1)
#define len(x) (T[x].r-T[x].l+1)
#define mid ((T[root].l+T[root].r)>>1)
#define ll long long
using namespace std;
const int maxn=5e4+10;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
int n,m,op,a,b,c,cnt,ans[maxn];
struct que{int op,l,r,id;ll k;}Q[maxn],Q1[maxn],Q2[maxn];
struct node{int l,r;ll add,sum;}T[maxn<<2];
inline void pushup(int root){T[root].sum=T[lc].sum+T[rc].sum;}
inline void pushnow(int root,int val){
T[root].add+=val;
T[root].sum+=len(root)*val;
}
inline void pushdown(int root){
if(T[root].add){
pushnow(lc,T[root].add);
pushnow(rc,T[root].add);
T[root].add=0;
}
}
inline void Update(int root,int l,int r,int val){
if(l<=T[root].l&&T[root].r<=r)
return pushnow(root,val);
pushdown(root);
if(l<=mid) Update(lc,l,r,val);
if(r> mid) Update(rc,l,r,val);
pushup(root);
}
inline ll Query(int root,int l,int r,ll ret=0){
if(l<=T[root].l&&T[root].r<=r)
return T[root].sum;
pushdown(root);
if(l<=mid) ret+=Query(lc,l,r);
if(r> mid) ret+=Query(rc,l,r);
return ret;
}
inline void build(int root,int l,int r){
T[root].l=l,T[root].r=r;
if(l==r) return;
build(lc,l,mid),build(rc,mid+1,r);
}
inline void Solve(int l,int r,int S,int T){
if(l>r||S>T) return;
if(l==r){
for(int i=S;i<=T;++i) if(Q[i].op==2)
ans[Q[i].id]=l;
return;
}
int Mid=(l+r)>>1,num1=0,num2=0;
for(int i=S;i<=T;++i){
if(Q[i].op==1){
if(Q[i].k>Mid)
Update(1,Q[i].l,Q[i].r,1),Q2[++num2]=Q[i];
else Q1[++num1]=Q[i];
}
else{
ll tmp=Query(1,Q[i].l,Q[i].r);
if(tmp>=Q[i].k) Q2[++num2]=Q[i];
else Q[i].k-=tmp,Q1[++num1]=Q[i];
}
}
for(int i=1;i<=num2;++i) if(Q2[i].op==1) Update(1,Q2[i].l,Q2[i].r,-1);
for(int i=1;i<=num1;++i) Q[S+i-1]=Q1[i];
for(int i=1;i<=num2;++i) Q[S+num1+i-1]=Q2[i];
Solve(l,Mid,S,S+num1-1),Solve(Mid+1,r,S+num1,T);
}
int main(){
n=read(),m=read(),build(1,1,n);
for(int i=1;i<=m;++i){
Q[i].op=read(),Q[i].l=read(),Q[i].r=read(),Q[i].k=(ll)read();
if(Q[i].op==2) Q[i].id=++cnt;
}
Solve(1,n,1,m);
for(int i=1;i<=cnt;++i)
printf("%d\n",ans[i]);
}