1. 程式人生 > >BZOJ3203 SDOI2013保護出題人(三分)

BZOJ3203 SDOI2013保護出題人(三分)

for 保護 維護 pan stdout line main sdoi2013 style

  給a做一個前綴和,那麽現在每次所查詢的就是(sn-sk)/(bn+nd-(k+1)d)的最大值。這個式子可以看成是(bn+nd,sn)和((k+1)d,sk)所成直線的斜率。

  腦補一條直線不斷減小斜率,容易發現可能成為最大值的點一定在下凸殼上。並且凸殼上的點和該點間的斜率變化情況是一個凸函數。於是維護出凸殼,在凸殼上三分即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using
namespace std; #define N 100010 #define ll long long ll read() { ll 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; } int n,q[N],tail; double ans=0; ll d,a[N],b[N];
double calc(int x,int y) { return 1.0*(a[y]-a[x])/((y-x)*d); } double getans(int n,int k) { return 1.0*(a[n]-a[k])/(b[n]+(n-k-1)*d); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj3203.in","r",stdin); freopen("bzoj3203.out","w",stdout); const char LL[]="%I64d\n"; #else const char
LL[]="%lld\n"; #endif n=read(),d=read(); for (int i=1;i<=n;i++) a[i]=read(),b[i]=read(); q[tail=1]=0; for (int i=1;i<=n;i++) { a[i]+=a[i-1]; int l=1,r=tail; while (l+2<r) { int mid=l+r>>1; double x=getans(i,q[mid]),y=getans(i,q[mid+1]); if (x>y) r=mid+1;else l=mid; } int mx=l; if (l+1<=r&&getans(i,q[l+1])>getans(i,q[mx])) mx=l+1; if (l+2<=r&&getans(i,q[l+2])>getans(i,q[mx])) mx=l+2; ans+=getans(i,q[mx]); while (tail>=2&&calc(q[tail-1],q[tail])>calc(q[tail],i)) tail--; q[++tail]=i; } printf("%0.lf",ans); return 0; }

BZOJ3203 SDOI2013保護出題人(三分)