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;
}

浙公网安备 33010602011771号