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; }