题目:
2019牛客暑期多校训练营9H:Cutting Bamboos
题意:
给出一排竹子,然后给出Q次询问,每次询问你需要水平砍y刀将[L,R]区间里的竹子砍完,并且每次砍下的竹子高度和一样,问第x刀砍应在那里
分析:
由于每刀砍下的一样,那么就可以知道前x刀一共砍下的竹子,首先可以二分第x刀砍的位置h,只需快速check这一刀砍下的和即可
那么就相当于求一个区间内比h大的数的和 和 个数即可,这个直接主席树就行了
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+10;
const double eps = 1e-10;
int cnt,root[maxn],l,r,x,y,v;
LL per[maxn];
struct tree{
int l,r,num;
LL sum;
}T[maxn*30];
void BuildTree(int l,int r,int &x,int y,int val){
T[++cnt] = T[y];T[cnt].num++;T[cnt].sum+=val;x = cnt;
if(l == r) return ;
int mid = (l+r) >> 1;
if(val > mid) BuildTree(mid+1,r,T[x].r,T[y].r,val);
else BuildTree(l,mid,T[x].l,T[y].l,val);
}
void QueryTree(int l,int r,int x,int y,double h,LL &sum1,LL &sum2){
if(l > h){
sum1 += (T[x].num - T[y].num);
sum2 += (T[x].sum - T[y].sum);
return ;
}
int mid = (l+r)>>1;
if(mid>h) QueryTree(l,mid,T[x].l,T[y].l,h,sum1,sum2);
if(r > h) QueryTree(mid+1,r,T[x].r,T[y].r,h,sum1,sum2);
}
bool check(double h){
LL sum1 = 0, sum2 = 0;
QueryTree(1,maxn,root[r],root[l-1],h,sum1,sum2);
return sum2*1.0-sum1*h >= (per[r]-per[l-1])*1.0/y*x;
}
double solve(){
double L = 0.0,R = 100001.0;
while(fabs(R-L) > eps){
double mid = (L+R)/2.0;
if(check(mid)) L = mid;
else R = mid;
}
return L;
}
int main(){
int n,q; scanf("%d %d",&n,&q);
for(int i = 1;i <= n; ++i){
scanf("%d",&v); per[i] = per[i-1] + v;
BuildTree(1,maxn,root[i],root[i-1],v);
}
while(q--){
scanf("%d %d %d %d",&l,&r,&x,&y);
printf("%.10f\n",solve());
}
return 0;
}