zoukankan      html  css  js  c++  java
  • [BZOJ4476] [JSOI2015] 送礼物 (01分数规划+ST表)

    [BZOJ4476] [JSOI2015] 送礼物 (01分数规划+ST表)

    题面

    给出n,k,l,r和序列a,要求从a中选一段连续的区间[i,j]出来,使得M(i,j)-m(i,j)/(j-i+k)最大,且[i,j]长度在区间[l,r]内。
    M(i,j)表示[i,j]中的最大值,m(i,j)表示[i,j]中的最小值。
    Ai< =10^8,N,K< = 50,000

    分析

    看到(frac{max(i,j)-min(i,j)}{j-i+k})的形式,我们敏感的想到01分数规划。考虑最终答案我们取的最大值和最小值在序列中的位置。

    1.如果最大值和最小值在序列中的距离<L,那么用长度为L的区间包含它最优。因为如果区间更长的话,分子不变,分母会变大,不优。这种情况区间长度已经确定,可以不用01分数规划,直接用单调队列(O(n))求即可。类似滑动窗口问题。

    2.如果最大值和最小值在序列中的距离>=L,那么它们在区间的左右端点最优。同样如果区间更长的话,分子不变,分母会变大,不优。

    按照01分数规划的套路,二分答案mid,判断当前答案是比mid大还是比mid小。如果比mid大,则有
    (|a_i-a_j| geq mid(j-i+k))

    (1)$ (a[i]+mid imes i)-(a[j]+mid imes j) geq mid imes k $,

    (区间左端点为最大值)只要暴力枚举i,再在i右侧满足区间长度条件的范围[i+L-1,i+R-1]内求一个(a[j]+mid imes j)最小的j即可

    (2)$ (a_j-mid imes j)-(a_i-mid imes i) geq mid imes k ,$

    (区间右端点为最大值)只要暴力枚举j,再在j左侧满足区间长度条件的范围[i-L+1,i-R+1]内求一个(a[j]+mid imes j)最小的j即可

    如果[i,j]内有比a[i]更大的a[k]怎么办?枚举到k的时候就会计入答案,且k为左端点的答案肯定比i优,会覆盖掉i为左端点的不合法情况
    用ST表维护最小值即可。

    时间复杂度(O(n log ^2 n)),因为每次检查mid都要重新建一次ST表

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<cmath> 
    #include<deque>
    #define INF 1e9
    #define eps 1e-6 
    #define maxn 50000
    #define maxlogn 20
    using namespace std;
    inline void qread(int &x){
    	x=0;
    	int sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9'){
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9'){
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign; 
    }
    int t;
    int n,K,L,R;
    int a[maxn+5];
    
    
    //最大值最小值在两端的情况 
    //最大值最小值在区间两端的时候显然更优 
    // 01分数规划的套路,二分答案mid
    //|a[i]-a[j]| >=mid(j-i+k)
    //(1) (a[i]+mid*i)-(a[j]+mid*j)>=mid*k , 只要暴力枚举i,再在i右侧满足区间长度条件的范围内求一个值最小的j
    //(2) (a[j]-mid*j)-(a[i]+mid*i)>=mid*k , 只要暴力枚举j,再在j左侧满足区间长度条件的范围内求一个值最小的j
    //如果[i,j]内有比a[i]更大的a[k]怎么办?枚举到k的时候就会计入答案,且k为左端点的答案肯定比i优,会覆盖掉i为左端点的不合法情况 
    int log_2[maxn+5]; 
    double st1[maxlogn+5][maxn+5];//维护a[i]+mid*i的最小值 
    double st2[maxlogn+5][maxn+5];//维护a[i]-mid*i的最小值 
    inline double max(double a,double b){
    	return a>b?a:b;
    }
    inline double min(double a,double b){
    	return a<b?a:b;
    }
    double query1(int l,int r){
    	int k=log_2[r-l+1];
    	return min(st1[k][l],st1[k][r-(1<<k)+1]);
    } 
    double query2(int l,int r){
    	int k=log_2[r-l+1];
    	return min(st2[k][l],st2[k][r-(1<<k)+1]);
    } 
    bool check(double mid){
    	for(int i=1;i<=n;i++) st1[0][i]=a[i]+mid*i;
    	for(int i=1;i<=n;i++) st2[0][i]=a[i]-mid*i;
    	for(int j=1;(1<<j)<=n;j++){
    		for(int i=1;i+(1<<j)-1<=n;i++){
    			st1[j][i]=min(st1[j-1][i],st1[j-1][i+(1<<(j-1))]);
    			st2[j][i]=min(st2[j-1][i],st2[j-1][i+(1<<(j-1))]);
    		}
    	} 
    	double ans=-INF;
    	for(int i=1;i<=n;i++){
    		if(i+L-1<=n) ans=max(ans,a[i]+mid*i-query1(i+L-1,min(i+R-1,n))); 
    	}
    	for(int i=1;i<=n;i++){//这里的i对应的是上面的j 
    		if(i-L+1>=1) ans=max(ans,a[i]-mid*i-query2(max(i-R+1,1),i-L+1));
    	}
    	return ans>=mid*K;
    } 
    
    //区间长度为L的时候,区间最大值不一定在左右端点,也可能成为最大答案 
    //直接长度为L的滑块窗口求解 
    double sp_solve(){
    	deque<int>qmax;
    	deque<int>qmin;
    	double ans=-INF;
    	for(int i=1;i<=n;i++){
    		while(!qmax.empty()&&qmax.front()<i-L+1) qmax.pop_front(); 
    		while(!qmin.empty()&&qmin.front()<i-L+1) qmin.pop_front(); 
    		while(!qmax.empty()&&a[qmax.back()]<a[i]) qmax.pop_back();
    		qmax.push_back(i);
    		while(!qmin.empty()&&a[qmin.back()]>a[i]) qmin.pop_back();
    		qmin.push_back(i);
    		ans=max(ans,1.0*(a[qmax.front()]-a[qmin.front()])/(L-1+K));
    	}
    	return ans;
    }
    double bin_search(){
    	double l=0,r=1000;
    	double mid;
    	double res=0; 
    //	printf("db %d
    ",check(0+eps)?1:0);
    	while(r-l>=eps){
    		mid=(l+r)/2;
    		if(check(mid)){
    			res=mid;
    			l=mid+eps;
    		}
    		else r=mid-eps;
    	}
    	return res;
    }
    int main(){
    //	freopen("1.in","r",stdin);
    	log_2[0]=-1;
    	for(int i=1;i<=maxn;i++) log_2[i]=log_2[i>>1]+1;
    	qread(t);
    	while(t--){
    		qread(n);
    		qread(K);
    		qread(L);
    		qread(R);
    		for(int i=1;i<=n;i++) qread(a[i]);
    		double ans1=sp_solve();
    //		printf("ans1=%.4f
    ",ans1);
    		double ans2=bin_search();
    		double ans=max(ans1,ans2); 
    		printf("%.4f
    ",ans);
    	}
    
    } 
    
  • 相关阅读:
    【BZOJ 4581】【Usaco2016 Open】Field Reduction
    【BZOJ 4582】【Usaco2016 Open】Diamond Collector
    【BZOJ 4580】【Usaco2016 Open】248
    【BZOJ 3754】Tree之最小方差树
    【51Nod 1501】【算法马拉松 19D】石头剪刀布威力加强版
    【51Nod 1622】【算法马拉松 19C】集合对
    【51Nod 1616】【算法马拉松 19B】最小集合
    【51Nod 1674】【算法马拉松 19A】区间的价值 V2
    【BZOJ 2541】【Vijos 1366】【CTSC 2000】冰原探险
    【BZOJ 1065】【Vijos 1826】【NOI 2008】奥运物流
  • 原文地址:https://www.cnblogs.com/birchtree/p/11360244.html
Copyright © 2011-2022 走看看