1. 程式人生 > >BZOJ 4476送禮物

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