zoukankan      html  css  js  c++  java
  • 【BZOJ4476】[Jsoi2015]送礼物 分数规划+RMQ

    【BZOJ4476】[Jsoi2015]送礼物

    Description

    JYY和CX的结婚纪念日即将到来,JYY来到萌萌开的礼品店选购纪念礼物。
    萌萌的礼品店很神奇,所有出售的礼物都按照特定的顺序都排成一列,而且相邻的礼物之间有一种神秘的美感。于是,JYY决定从中挑选连续的一些礼物,但究竟选哪些呢?
    【问题描述】
    假设礼品店一共有N件礼物排成一列,每件礼物都有它的美观度。排在第i1< =i< =N个位置的礼物美观度为正整数Ai,。JYY决定选出其中连续的一段,即编号为礼物i,i+1,…,j-1,j的礼物。选出这些礼物的美观程度定义为
    (M(i,j)-m(i,j))/(j-i+k)
    其中M(i,j)表示max{Ai,Ai+1....Aj},m(i,j)表示min{Ai,Ai+1....Aj},K为给定的正整数。
    由于不能显得太小气,所以JYY所选礼物的件数最少为L件;同时,选得太多也不好拿,因此礼物最多选R件。JYY应该如何选择,才能得到最大的美观程度?由于礼物实在太多挑花眼,JYY打算把这个问题交给会编程的你。

    Input

    本题每个测试点有多组数据。输入第一行包含一个正整数T(T< =10),表示有T组数据。
    每组数据包含两行,第一行四个非负整数N,K,L,R(2< =L< =R< =N。第二行包含N个正整数,依次表示A1,A2....An,(Ai< =10^8),N,K< = 50,000

    Output

    输出T行,每行一个非负实数,依次对应每组数据的答案,数据保证答案不会超过10^3。输出四舍五入保留4位小数。

    Sample Input

    1
    5 1 2 4
    1 2 3 4 5

    Sample Output

    0.7500

    题解:显然先分数规划,然后根据贪心,选出来的区间的最大和最小值一定是在两端的,设最大值为v[i],最小值为v[j],所以式子就变成:

    v[i]-v[j]-(i-j+k)*mid>0或v[i]-v[j]-(j-i+k)*mid>0

    然后分开讨论,第一个变成求(v[i]-i*mid)-(v[j]-j*mid)的最大值,第二个变成求(v[i]+i*mid)-(v[j]+j*mid)的最大值,题解说可以用单调队列搞定,但是我比较懒,直接用的RMQ。

    但是感觉不对?当最大最小值的距离<L时,我们也要将长度视为L,即v[i]-v[j]-(L-1+k)*mid>0,同理,求v[i]-v[j]的最大值即可,依旧RMQ。

    所以。。。所以RMQ比单调队列慢。。。所以又光荣的变成status倒数第一,并且时间也很吉利~

    (时限30s)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const double eps=1e-7;
    const int maxn=100010;
    int n,K,L,R,h,t;
    int v[maxn],Log[maxn];
    double f1[18][maxn],f2[18][maxn];
    int f[18][maxn];
    int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    double q1(int l,int r)
    {
    	int k=Log[r-l+1];
    	return min(f1[k][l],f1[k][r-(1<<k)+1]);
    }
    double q2(int l,int r)
    {
    	int k=Log[r-l+1];
    	return min(f2[k][l],f2[k][r-(1<<k)+1]);
    }
    int q(int l,int r)
    {
    	int k=Log[r-l+1];
    	return min(f[k][l],f[k][r-(1<<k)+1]);
    }
    bool solve(double sta)
    {
    	int i,j;
    	double ret=-99999999.9999;
    	for(i=1;i<=n;i++)	f1[0][i]=v[i]-sta*i,f2[0][i]=v[i]+sta*i;
    	for(j=1;(1<<j)<=n;j++)	for(i=1;i+(1<<j)-1<=n;i++)
    		f1[j][i]=min(f1[j-1][i],f1[j-1][i+(1<<j-1)]),f2[j][i]=min(f2[j-1][i],f2[j-1][i+(1<<j-1)]);
    	for(i=1;i<=n;i++)
    	{
    		if(i>=L)	ret=max(ret,f1[0][i]-q1(max(1,i-R+1),i-L+1));
    		if(i<=n-L+1)	ret=max(ret,f2[0][i]-q2(i+L-1,min(n,i+R-1)));
    	}
    	for(i=1;i<=n;i++)
    	{
    		ret=max(ret,v[i]-q(max(1,i-L+1),min(n,i+L-1))-sta*(L-1));
    	}
    	return ret>=K*sta;
    }
    void work()
    {
    	n=rd(),K=rd(),L=rd(),R=rd();
    	int i,j;
    	for(i=1;i<=n;i++)	f[0][i]=v[i]=rd();
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	for(j=1;(1<<j)<=n;j++)	for(i=1;i+(1<<j)-1<=n;i++)	f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]);
    	double l=0,r=1000,mid;
    	while(r-l>eps)
    	{
    		mid=(l+r)/2;
    		if(solve(mid))	l=mid;
    		else	r=mid;
    	}
    	printf("%.4lf
    ",l);
    	return ;
    }
    int main()
    {
    	int T=rd();
    	while(T--)	work();
    	return 0;
    }

     

  • 相关阅读:
    c++运算符重载
    c++ const_cast
    SHL
    C++拷贝构造函数(深拷贝,浅拷贝)
    ps命令详解
    static 修饰符
    “宝洁八大问”整理篇
    linux grep命令
    C++操作符重载
    linux中删除指定日期之前的文件
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7246831.html
Copyright © 2011-2022 走看看