2019牛客多校第九場
阿新 • • 發佈:2019-08-15
H Cutting Bamboos
題意
給n個高度,每次獨立詢問一個區間\([l,r]\),對於這個區間的所有高度,要求砍\(y\)次剛好全部砍完,問第\(x\)次砍的位置。
分析
- 可以二分砍的位置,計算出第\(x\)次砍掉的所有高度,進行check,所以問題就轉化為如何求區間\([l,r]\)裡大於某個值的高度差和。
- 顯然只需要用主席樹維護值域每個數的個數和加和,查詢區間大於某個值\(x\)的加和,再減去大於\(x\)的個數乘以\(x\),即可。
程式碼
#include <bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
typedef long long ll;
typedef double db;
typedef pair<ll,ll> pll;
const int N=2e5+50;
const db eps=1e-8;
int n,q;
ll h[N],p[N];
int l,r;
ll x,y;
int cnt,tr[N],lr[N*40],rr[N*40];
ll num[N*40],sum[N*40];
int build(int l,int r){
int rt=++cnt;
sum[rt]=num[rt]=0;
if(l==r){
return rt;
}
lr[rt]=build(l,mid);
rr[rt]=build(mid+1,r);
return rt;
}
int insert(int pre,int l,int r,int v){
int rt=++cnt;
lr[rt]=lr[pre];
rr[rt]=rr[pre];
num[rt]=num[pre]+1;
sum[rt]=sum[pre]+v;
if(l==r){
return rt;
}
if(v<=mid){
lr[rt]=insert(lr[pre],l,mid,v);
}else{
rr[rt]=insert(rr[pre],mid+1,r,v);
}
return rt;
}
//查詢區間大於等於q的h個數和總和
pll query(int u,int v,int l,int r,int q){
if(l>=q){
return {num[v]-num[u],sum[v]-sum[u]};
}
if(q<=mid){
auto p=query(lr[u],lr[v],l,mid,q);
return {p.first+num[rr[v]]-num[rr[u]],p.second+sum[rr[v]]-sum[rr[u]]};
}else{
return query(rr[u],rr[v],mid+1,r,q);
}
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&q);
ll mx=0;
for(int i=1;i<=n;i++){
scanf("%lld",&h[i]);
mx=max(mx,h[i]);
p[i]=p[i-1]+h[i];
}
tr[0]=build(1,mx);
for(int i=1;i<=n;i++){
tr[i]=insert(tr[i-1],1,mx,h[i]);
}
for(int i=0;i<q;i++){
scanf("%d%d%lld%lld",&l,&r,&x,&y);
db ned=(p[r]-p[l-1])*1.0/y*x;
db L=0.0,R=mx*1.0;
while(L+eps<=R){
db M=(L+R)/2;
auto t=query(tr[l-1],tr[r],1,mx,int(ceil(M)));
db k=t.second*1.0-t.first*M;
if(k<ned){
R=M-eps;
}else{
L=M+eps;
}
}
printf("%.12lf\n",L);
}
return 0;
}