题意:
有n个竹子,每个竹子有自己的高度
h
[
i
]
h[i]
h[i],
h
[
i
]
∈
[
1
,
100000
]
h[i]\in[1,100000]
h[i]∈[1,100000]
有q个询问,格式为l r x y
询问l到r区间内割y刀,第x刀的高度在哪。
思路:
以竹子的下标作为版本建主席树,把高度作为另一维(也就是主席树上的l,r),根据高度维护竹子的个数和高度和。
最后二分切的高度,记一个二分的高度h,然后找出小于等于h的竹子有x个,以及这些竹子的高度和sum_h1。区间内总竹子个数减去小于等于h的竹子的个数就是高于h的竹子个数了,也就是r-l+1-x。最后剩下的竹子高度之和为sum_h1+((r-l+1)-x)*k。然后拿这个去跟直接计算出的高度比较(
(
∑
i
∈
[
l
,
r
]
h
[
i
]
)
/
y
∗
(
y
−
x
)
(\sum_{i\in[l,r]}h[i])/y*(y-x)
(∑i∈[l,r]h[i])/y∗(y−x)),如果剩下来的大于这个值,那么h还要再往下放。
(貌似可以直接计算出来,就不用二分了,复杂度降一个log)
参考代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll sh[N];
int root[N],lson[N*20],rson[N*20],tot;
ll sum[N*20],sz[N*20];
void update(int last,int cur,int l,int r, int k){
rson[cur]=rson[last];
lson[cur]=lson[last];
sum[cur]=sum[last]+k;
sz[cur]=sz[last]+1;
if(l==r)return;
int mid=(l+r)>>1;
if(k<=mid)update(lson[last],lson[cur]=++tot,l,mid,k);
else update(rson[last],rson[cur]=++tot,mid+1,r,k);
}
ll a_sum,a_num;
void query(int last,int cur,int l,int r,int m){
if(m>=r){
a_sum+=sum[cur]-sum[last];
a_num+=sz[cur]-sz[last];
return;
}
int mid=(l+r)>>1;
if(l<=m)query(lson[last],lson[cur],l,mid,m);
if(mid+1<=m)query(rson[last],rson[cur],mid+1,r,m);
}
int main(){
int n,q;
scanf("%d%d",&n,&q);
int h;
tot=0;
for(int i=1;i<=n;i++){
scanf("%d",&h);
update(root[i-1],root[i]=++tot,1,100000,h);
sh[i]=sh[i-1]+1ll*h;
}
int l,r,x,y;
for(int i=1;i<=q;i++){
scanf("%d%d%d%d",&l,&r,&x,&y);
double li=0,ri=100000.0,eps=1e-7,mid;
while (fabs(ri-li)>eps){
mid=(li+ri)/2.0;
a_sum=0;
a_num=0;
query(root[l-1],root[r],1,100000,(int)mid);
a_num=1ll*(r-l+1)-a_num;
double s=mid*a_num+1.0*a_sum;
double cnt=1.0*(sh[r]-sh[l-1])/y*(y-x);
if(cnt<s)ri=mid;
else li=mid;
}
printf("%.7f\n",li);
}
return 0;
}