BZOj 4476:送礼物

Portal:http://www.lydsy.com/JudgeOnline/problem.php?id=4476

解析:

首先看到分数形式我们想到分数规划,先二分答案,设当前答案为\(ans\),问题变为判断是否存在\(l,r\),满足\(M[l,r]-m[l,r]-ans(r-l+k) \geq0\)

这个问题有一个性质:若最优区间的长度大于\(L\),则区间的两端一定为此区间的最大值和最小值(否则此区间可变短,与最优矛盾)。

所以我们在二分前先对所有长度为L的区间求出最优解,将它作为二分的下界,对于每次二分只要求出区间长度大于\(L\)的最优解,可以用单调队列在\(O(n)\)时间内解决(维护\(M[l,r]-ans[r+k]\),正反循环两遍),总时间复杂度为\(O(nlogw)\)

注意精度。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm> 
#include <vector> 

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())

using namespace std;

typedef long long LL;

const int maxn=100000+21,maxlogn=15+1;
const double eps=1e-6;

inline int read() {
 int x=0; char c=getchar();
 while ((c<'0')||(c>'9')) c=getchar();
 while (('0'<=c)&&(c<='9')) x=x*10+(int)(c-'0'),c=getchar();
 return x;
}

int T,n,K,Lm,Rm,l,r,k,num[maxn];

double L,R,mid,a[maxn],maxi[maxn][maxlogn],mini[maxn][maxlogn];

pair<int,double> Q[maxn];

void init()
{
 k=0;
 rep(i,1,n)
 {
  if ((1<<(k+1))<i) k++;
  num[i]=k;
 }

 rep(i,1,n) mini[i][0]=maxi[i][0]=a[i];

 rep(k,1,maxlogn-1)
  rep(i,1,n)
  {
   mini[i][k]=min(mini[i][k-1],mini[i+(1<<(k-1))][k-1]);
   maxi[i][k]=max(maxi[i][k-1],maxi[i+(1<<(k-1))][k-1]);
  }
}

inline double query_min(int l,int r)
{
 int k=num[r-l+1];
 return min(mini[l][k],mini[r-(1<<k)+1][k]);
}

inline double query_max(int l,int r)
{
 int k=num[r-l+1];
 return max(maxi[l][k],maxi[r-(1<<k)+1][k]);
}

inline void add(int x,double k,int ind,double ans)
{
 double cur=k-(double)(Lm)*ans;
 while ((l<=r)&&(cur>=(Q[r].second-((abs(ind-Q[r].first)+1)*ans)))) r--; //注意绝对值
 Q[++r]=make_pair(x,k);
}

inline bool check(double ans)
{
 l=1,r=0;
 rep(i,Lm,n) //正着循环
 {
  while ((l<=r)&&((i-Q[l].first+1)>Rm)) l++;
  add(i-Lm+1,a[i-Lm+1],i,ans); //ind + val 
  
  if ((Q[l].second-(i-Q[l].first+K)*ans-a[i])>=0) return true;
 }
 
 l=1,r=0; 
 dep(i,n-Lm+1,1) //反着循环
 {
  while ((l<=r)&&((Q[l].first-i+1)>Rm)) l++;
  add(i+Lm-1,a[i+Lm-1],i,ans); //ind + val
  
  if ((Q[l].second-(Q[l].first-i+K)*ans-a[i])>=0) return true;
 }

 return false;
}

int main()
{
 T=read();
 while (T--)
 {
  n=read(),K=read(),Lm=read(),Rm=read();
  
  L=R=0;
  rep(i,1,n) a[i]=(double)(read()),R=max(R,a[i]);
  
  init();
  
  rep(i,1,n-Lm+1)
  {
   double mini=query_min(i,i+Lm-1),maxi=query_max(i,i+Lm-1);
   L=max(L,((maxi-mini)/(double)(Lm+K-1))); //区间长为L的最优解
  }

  while ((R-L)>=eps) //二分答案
  {
   mid=(L+R)/2;
   if (check(mid)) L=mid; else R=mid;
  }

  printf("%.4f\n",L);
 }

 return 0;
}

posted @ 2017-02-07 21:28  Krew  阅读(185)  评论(0)    收藏  举报