有N个位置,M个操作。操作有两种,每次操作是:
- 1 a b c:表示在第a个位置到第b个位置,每个位置加上一个数c
- 2 a b c:表示询问从第a个位置到第b个位置,第C大的数是多少。
算法见注释
#include<cstdio>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m,qqq;
struct data{
int t,a,b,c,idx;
void read(){
scanf("%lld%lld%lld%lld",&t,&a,&b,&c);
if(t==2)idx=++qqq;
}
};
data q[N];
int ans[N];
struct BIT{//树状数组
int c[N];
void add(int pos,int v){
for(int i=pos;i<=n;i+=i&-i)c[i]+=v;
}
int pre(int pos){
int res=0;
for(int i=pos;i>=1;i-=i&-i)res+=c[i];
return res;
}
};
BIT d,dm;
void add(int l,int r,int v){
d.add(l,v), d.add(r+1,-v);
dm.add(l,v*l), dm.add(r+1,-v*(r+1));
}
int pre(int pos){return (pos+1)*d.pre(pos)-dm.pre(pos);}
int sum(int l,int r){return pre(r)-pre(l-1);}
data q1[N],q2[N];
void solve(int l,int r,int L,int R){
if(L==R){
for(int i=l;i<=r;i++)if(q[i].t==2)ans[q[i].idx]=L;
return;
}
int Mid=(L+R)>>1,l1=0,l2=0;
for(int i=l;i<=r;i++){//计算在[Mid+1,R]区间的数的个数(contri)
data now=q[i];
if(now.t==1){
//只加入[Mid+1,R]的贡献
if(Mid<now.c&&now.c<=R)add(now.a,now.b,1), q2[++l2]=now;
else q1[++l1]=now;
} else {
int t=sum(now.a,now.b);
if(now.c<=t)q2[++l2]=now;
else now.c-=t, q1[++l1]=now;
}
}
for(int i=1;i<=l2;i++)if(q2[i].t==1)add(q2[i].a,q2[i].b,-1);//还原
for(int i=1;i<=l1;i++)q[l+i-1]=q1[i];
for(int i=1;i<=l2;i++)q[l+l1+i-1]=q2[i];
solve(l,l+l1-1,L,Mid);
solve(l+l1,r,Mid+1,R);
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)q[i].read();
solve(1,m,0,n);
for(int i=1;i<=qqq;i++)printf("%lld
",ans[i]);
return 0;
}
/*
* 考虑整体二分。二分答案后,问题转化为
* - 查询比mid大的数
* - 区间添加数
* 整体二分是要求贡献独立的,因此在二分的过程中只考虑跨区间的贡献,并且
* 必须全部统计完
*/