BZOJ 4476送礼物

    题目大意:一个整数序列n,给定一个长度范围L~R,给定一个K,让求一个ans,ans=max(ans,(max(l,r)-min(l,r))/(r-l+k))。r-l+1在L到R之内。

    仔细分析这是一道分数规划题目。首先这个答案肯定是单调的,我们可以二分答案,把求最优值的问题转化为判定性问题。那么我们只差一个check函数。我们二分一个mid,如果不考虑区间长度在L到R之内的话,最优的区间一定是左右两个端点正好是最值。那么我们可以分类讨论。如果a[r]>a[l],如果mid是最终答案,那么一定满足所有的(a[r]-a[l])/(r-l+k)<=mid,把(r-l+k)移过去,a[r]-rmid-(a[l]-lmid)<=kmid,这种情况下,显然我们可以维护一个单调队列,队头是关于i符合区间长度的a[l]-mid的最小值,然后每次更新答案的时候,我们需要先找到队头到i到第一个符合右端点是最大值的点,只需要用一个lower_bound就可以了。这样我们一定保证了右端点是最大值,然后每次答案是当前情况的最优值。对于a[l]>a[r]的情况同理。

    当然,我们还有一种情况没有分析到。就是有可能那些符合左右端点都正好是最值的区间的长度都小于L,那么我们此时枚举长度为L的分别求一遍,肯定是最优的。所以我们可以用rmq求一下所有区间长度为L的最大值,当然仍然使用单调队列更优。

    这道题写了很长时间,然后在BZOJ上Wa了6次,不知道哪儿出了问题,最后发现t组数据没有初始化ans(真是傻逼了)

#include<bits/stdc++.h>
#define eps 1e-6
using namespace std;
inline int read(){
	char ch=getchar();int num=0,f=1;
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){num=(num<<1)+(num<<3)+ch-'0';ch=getchar();}
	return num*f;
}
const int N=1e6+10;
int t,n,k,l,r,head1,head2,tail1,tail2,head,tail,q1[N],q2[N],s[N],top,a[N];
double ans,f[N];
struct node{
	int id;double val;
}q[N];
bool cmp(node x,node y){
	return x.id<y.id;
}
bool check(double mid){
	double mx=0;
	head=tail=top=1;
	for(int i=1;i<=n;++i) f[i]=(double)a[i]-mid*i;
	q[1].id=1;q[1].val=f[1];s[1]=1;
	for(int i=2;i<=n;++i){
		while(top&&a[s[top]]<=a[i]) top--;
		s[++top]=i;
		while(head<=tail&&q[head].id<i-r+1) head++;
		node t;t.id=s[top-1]+1;
		int w=lower_bound(q+head,q+tail+1,t,cmp)-q;
		if(w<=tail&&q[w].id<=i-l+1) mx=max(mx,f[i]-q[w].val);
		while(head<=tail&&f[i]<q[tail].val) tail--;
		q[++tail].val=f[i];q[tail].id=i;
	}
	head=tail=top=1;
	for(int i=1;i<=n;++i) f[i]=(double)a[i]+mid*i;
	q[1].id=1;q[1].val=f[1];s[1]=1;
	for(int i=2;i<=n;++i){
		while(top&&a[s[top]]>=a[i]) top--;
		s[++top]=i;
		while(head<=tail&&q[head].id<i-r+1) head++;
		node t;t.id=s[top-1]+1;
		int w=lower_bound(q+head,q+tail+1,t,cmp)-q;
		if(w<=tail&&q[w].id<=i-l+1) mx=max(mx,q[w].val-f[i]);
		while(head<=tail&&f[i]>q[tail].val) tail--;
		q[++tail].val=f[i];q[tail].id=i;
	}
	if(mx-k*mid>=eps) return 1;
	else return 0;
}
int main(){
	t=read();
	while(t--){
		ans=0;
		n=read(),k=read(),l=read(),r=read();
		for(int i=1;i<=n;++i) a[i]=read();
		head1=head2=1;tail1=tail2=0;
		for(int i=1;i<l;++i){
			while(head1<=tail1&&a[q1[tail1]]<a[i]) tail1--;
			q1[++tail1]=i;
			while(head2<=tail2&&a[q2[tail2]]>a[i]) tail2--;
			q2[++tail2]=i;
		}
		for(int i=l;i<=n;++i){
			if(head1<=tail1&&q1[head1]<i-l+1) head1++;
			if(head2<=tail2&&q2[head2]<i-l+1) head2++;
			while(head1<=tail1&&a[q1[tail1]]<a[i]) tail1--;
			q1[++tail1]=i;
			while(head2<=tail2&&a[q2[tail2]]>a[i]) tail2--;
			q2[++tail2]=i;
			ans=max(ans,(double)1.0*(a[q1[head1]]-a[q2[head2]])/(l-1+k));
		}
		double L=0,R=1000;
		while(L+eps<=R){
			double mid=((L+R)*0.5);
			if(check(mid)) ans=max(ans,mid),L=mid;
			else R=mid;
		}
		printf("%.4lf\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39759315/article/details/83756762