二分第x次砍的位置,然后用线段树查询小于这个位置的数的个数和值的和。然后判断即可
注意!!!主席树是通过动态开点实现的,本身已经不用再从1开始了,而本题开的范围也应该是0,100000 而不是1,100000(害得我找了很久的错误)
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> #include<math.h> #define LL long long using namespace std; const int maxx = 200007; const double eps = 1e-10; struct node{ LL l,r,cnt; LL w; }tree[maxx<<5]; LL root[maxx]; LL a[maxx]; LL pre[maxx]; LL cnt; LL num; LL sumlength; void inserts(LL l,LL r,LL pre,LL &now,LL pos){ now=++cnt; tree[now]=tree[pre]; tree[now].cnt++; tree[now].w=tree[now].w+pos; if (l==r){ return; } LL mid=(l+r)>>1; if (pos<=mid){ inserts(l,mid,tree[pre].l,tree[now].l,pos); }else { inserts(mid+1,r,tree[pre].r,tree[now].r,pos); } } void query(LL L,LL R,LL l,LL r,LL pos){ // if (l>pos)return; if (l==r){ num+=(tree[R].cnt-tree[L].cnt); sumlength+=(tree[R].w-tree[L].w); return ; } LL mid=(l+r)>>1; if (pos<=mid){ query(tree[L].l,tree[R].l,l,mid,pos); }else{ num+=(tree[tree[R].l].cnt-tree[tree[L].l].cnt); sumlength+=(tree[tree[R].l].w-tree[tree[L].l].w); query(tree[L].r,tree[R].r,mid+1,r,pos); } } int main(){ LL n,q; while(~scanf("%lld%lld",&n,&q)){ pre[0]=0; cnt=0; for (int i=1;i<=n;i++){ scanf("%lld",&a[i]); pre[i]=pre[i-1]+a[i]; inserts(0,100000,root[i-1],root[i],a[i]); } LL ll,rr; LL x,y; while(q--){ scanf("%lld%lld%lld%lld",&ll,&rr,&x,&y); double l=0,r=100000; double ans=0; while(fabs(r-l)>eps){ double mid=(l+r)/2; num=0; sumlength=0; LL ss=floor(mid); query(root[ll-1],root[rr],0,100000,ss); num=(rr-ll+1)-num; double s=num*mid+sumlength; double step=1.0*(pre[rr]-pre[ll-1])/y; if (s-step*(y-x)>eps){ r=mid; }else{ l=mid; } } printf("%.10f\n",l); } } return 0; }