题目
分析
线段树上二分,二分答案,线段树。
首先,我们的目的是要求出尽可能小的最大体积,这显然是可以二分后直接判断的。
但是这样是 \(2\log\) 的,我们可以直接考虑线段树上二分。
可是这样就需要实数的线段树上二分了吗?并不是。
我们考虑求答案的过程:先把要选的试管全部调整到同一个体积,再同时给所有的试管平均倒水。
显然,因为我们的修改操作只会出现整数的体积,所以这个调整到的体积一定是一个整数!
所以我们可以考虑并不直接线段树上二分出答案,而是二分出这个调整到的高度!
二分过程如下,每次先重置 \(SIZE,SUM=0\) :
其中 \(sum\) 表示值域区间内水的体积和,\(siz\) 表示值域区间内试管的个数。
\(SUM\) 表示左侧总的水的体积和,\(SIZE\) 表示左侧总的试管个数
ll SIZE,SUM;
ll Query(int x,int l,int r,ll lim){
if(!x) return l;
if(l==r) return SIZE+=siz(x),SUM+=sum(x),l;
int mid=l+r>>1,MID=mid+1;//注意MID
ll res=1ll*(siz(ls(x))+SIZE)*mid-(sum(ls(x))+SUM);
if(res>=lim) return Query(ls(x),l,mid,lim);
else return SIZE+=siz(ls(x)),SUM+=sum(ls(x)),Query(rs(x),mid+1,r,lim);
}
接下来就是同时往上调整:
else{
read(l);//l是要分配的体积
SIZE=SUM=0;
int height=Query(Root,0,1e9,l);
l=l-(SIZE*height-SUM);//现在l是多余的拿来分配的体积
double Ans=(double)height+1.0*(double)l/(double)(SIZE);//加上多增加的高度
printf("%.5lf\n",Ans);
}
那么就结束了,这里我们通过调整二分的对象规避了实数的线段树上二分。
时间复杂度 \(O(n\log n)\) 。
代码
跑得比较快。
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template<typename T>
inline void write(T x){
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
#define ull unsigned long long
#define inc(x,y,mod) (((x)+(y))>=(mod)?(x)+(y)-(mod):(x)+(y))
#define dec(x,y,mod) ((x)-(y)<0?(x)-(y)+(mod):(x)-(y))
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dep(i,y,x) for(register int i=(y);i>=(x);i--)
const int N=1e5+5,M=2e5+5,MOD=1e9+7;
int n,m,h[N],cur;
struct SGT{
int siz,ls,rs;
ll sum;
#define ls(x) t[x].ls
#define rs(x) t[x].rs
#define sum(x) t[x].sum
#define siz(x) t[x].siz
}t[N<<6];
void Modify(int &x,int l,int r,int pos,int v){
if(!x) x=++cur;
siz(x)+=v,sum(x)+=v*pos;
if(l==r) return ;
int mid=l+r>>1;
if(pos<=mid) Modify(ls(x),l,mid,pos,v);
else Modify(rs(x),mid+1,r,pos,v);
return ;
}
ll SIZE,SUM;
ll Query(int x,int l,int r,ll lim){
if(!x) return l;
if(l==r) return SIZE+=siz(x),SUM+=sum(x),l;
int mid=l+r>>1,MID=mid+1;
ll res=1ll*(siz(ls(x))+SIZE)*MID-(sum(ls(x))+SUM);
if(res>=lim) return Query(ls(x),l,mid,lim);
else return SIZE+=siz(ls(x)),SUM+=sum(ls(x)),Query(rs(x),mid+1,r,lim);
}
int Root;
signed main(){
read(n);read(m);
rep(i,1,n) read(h[i]),Modify(Root,0,1e9,h[i],1);
while(m--){
ll op,l,r;
read(op);
if(op==1) read(l),read(r),Modify(Root,0,1e9,h[l],-1),Modify(Root,0,1e9,(h[l]=r),1);
else{
read(l);SIZE=SUM=0;
int height=Query(Root,0,1e9,l);
l=l-(SIZE*height-SUM);
double Ans=(double)height+1.0*(double)l/(double)(SIZE);
printf("%.5lf\n",Ans);
}
}
return 0;
}