1. 程式人生 > >BZOJ4476 JSOI2015送禮物(分數規劃+單調隊列)

BZOJ4476 JSOI2015送禮物(分數規劃+單調隊列)

即使 out nbsp 一次 隊列 const code 將他 printf

  看到這個式子當然先二分答案。得max-min-(j-i+k)ans>=0。

  顯然max-min相同的情況下所選區間長度越短越好,所以max和min都應該取在邊界。那麽實際上我們根本不用管端點是否真的是max或min,因為即使不是將他們計入也不會對最終答案造成影響。不妨設右端點是max,則要最大化aj-ai-(j-i)ans=(aj-jans)-(ai-ians),單調隊列維護即可。左端點是max同理。

  為了防止不存在長度在l~r的這樣的區間,先對長度l的區間單調隊列暴力跑一次。

#include<iostream> 
#include<cstdio>
#include
<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f; } #define N 50010 const double eps=1E-8; int T,n,k,l,r,a[N],p[N],q[N]; bool check(double ans) { int head=1,tail=0,head1=1,tail1=0; for (int i=1;i<=n;i++) { while (head<=tail&&p[head]<=i-l) head++; while (head<=tail&&a[i]>=a[p[tail]]) tail--; p[
++tail]=i; while (head1<=tail1&&q[head1]<=i-l) head1++; while (head1<=tail1&&a[i]<=a[q[tail1]]) tail1--; q[++tail1]=i; if (i>=l&&a[p[head]]-a[q[head1]]>=(l+k-1)*ans) return 1; } head=1,tail=0; for (int i=1;i<=n;i++) { while (head<=tail&&q[head]<=i-r) head++; if (i>=l) { while (head<=tail&&a[q[tail]]-q[tail]*ans>=a[i-l+1]-(i-l+1)*ans) tail--; q[++tail]=i-l+1; if ((a[i]-i*ans)-(a[q[head]]-q[head]*ans)>=k*ans) return 1; } } head=1,tail=0; for (int i=1;i<=n;i++) { while (head<=tail&&q[head]<=i-r) head++; if (i>=l) { while (head<=tail&&a[q[tail]]+q[tail]*ans<=a[i-l+1]+(i-l+1)*ans) tail--; q[++tail]=i-l+1; if ((a[q[head]]+q[head]*ans)-(a[i]+i*ans)>=k*ans) return 1; } } return 0; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4476.in","r",stdin); freopen("bzoj4476.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read(); while (T--) { n=read(),k=read(),l=read(),r=read(); for (int i=1;i<=n;i++) a[i]=read(); double L=0,R=1000,ans; while (L+eps<=R) { double mid=(L+R)/2; if (check(mid)) ans=mid,L=mid+eps; else R=mid-eps; } printf("%.4f\n",ans); } return 0; }

BZOJ4476 JSOI2015送禮物(分數規劃+單調隊列)