Cutting Bamboos【主席树+二分】

链接:https://ac.nowcoder.com/acm/contest/889/H
来源:牛客网

#include
using namespace std;
#define maxn 200010
typedef long long ll;
const double eps=1e-6;
struct Tree{
    ll l,r,sum;
	ll ans;
}tree[maxn*40];
ll a[maxn],b[maxn],sum[maxn];
ll n,m,rt[maxn],tot=0,cnt=0;
void build(int l,int r,ll &rt){
    rt=++tot; 
	tree[rt].sum=0;
	tree[rt].ans=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,tree[rt].l);
    build(mid+1,r,tree[rt].r);
}
void update(int l,int r,int c,int lrt,ll &rt){
    rt=++tot;
    tree[rt].l=tree[lrt].l;
    tree[rt].r=tree[lrt].r;
    tree[rt].sum=tree[lrt].sum+1;
    tree[rt].ans=tree[lrt].ans+b[c];
    if(l==r) return;
    int mid=(l+r)>>1;
    if(c<=mid) update(l,mid,c,tree[lrt].l,tree[rt].l);
    else update(mid+1,r,c,tree[lrt].r,tree[rt].r);
}
int query(int L,int R,int l,int r,int k){
    if(l==r) return b[l];
    int mid=(l+r)>>1;
    int ret=tree[tree[R].l].sum-tree[tree[L].l].sum; //表示rt[L]~rt[R]这个区间内左子树更新了多少个节点 
    if(k<=ret) return query(tree[L].l,tree[R].l,l,mid,k); 
    else return query(tree[L].r,tree[R].r,mid+1,r,k-ret);
}
ll query_sum(int L,int R,int l,int r,int k){//求前k小的和
	if(l==r) return 1ll*k*b[l];
	int mid=(l+r)>>1;
	int ret=tree[tree[R].l].sum-tree[tree[L].l].sum;
	if(k<=ret) return query_sum(tree[L].l,tree[R].l,l,mid,k);
	else{
		return tree[tree[R].l].ans-tree[tree[L].l].ans+query_sum(tree[L].r,tree[R].r,mid+1,r,k-ret);
	} 
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i]; b[i]=a[i];
		sum[i]=sum[i-1]+a[i];
    }
    sort(b+1,b+n+1);
    int cnt=unique(b+1,b+n+1)-(b+1);
    build(1,cnt,rt[0]);
    for(int i=1;i<=n;i++){
        int ret=lower_bound(b+1,b+cnt+1,a[i])-b;
        update(1,cnt,ret,rt[i-1],rt[i]);
    }
    while(m--){
        ll l,r,x,y; cin>>l>>r>>x>>y;
        double cutpart=double(sum[r]-sum[l-1])/y*x;
        ll L=0,R=r-l+1,K=0,ret=0; ll ans=0,res=0;
        while(L<=R){
        	ll mid=(L+R)>>1;
        	ret=query(rt[l-1],rt[r],1,cnt,mid);
        	ans=query_sum(rt[l-1],rt[r],1,cnt,mid);
        	res=sum[r]-sum[l-1]-ans-(r-l+1-mid)*ret;
        	if((double)res-eps>=cutpart){
        		K=mid; L=mid+1;
			}
			else R=mid-1;
		}
		double ans_sum=ret+((double)res-cutpart)/(r-l-K+1);
    	printf("%.10lf\n",ans_sum);
    }
    return 0;
}

你可能感兴趣的:(主席树)