BZOJ 3203 [SDOI2013]保护出题人 (凸包+三分)
题目大意:太长略
每新加入一个僵尸,容易得到方程$ans[i]=max{\frac{sum_{i}-sum_{j-1}}{s_{i}+d(i-j)}}$
即从头开始每一段僵尸都需要在规定距离内被消灭
展开式子,可得$ans[i]=max{\frac{sum_{i}-sum_{j-1}}{s_{i}+di-dj}}$
是不是很像斜率的式子= = ----$(y2-y1)/(x2-x2)$
维护一个下凸包,这次不是用直线去切凸包,而是把凸包上每个点都向一个定点去连直线,求最大的斜率
会发现,凸包上连出来的直线的斜率是一个凸函数,再利用三分法进行查找
三分法类似于一个爬坡的过程,每次都缩小两侧山坡范围,最终找到山顶
至于为什么维护下凸包呢,画个图就明白了,如果之前某个点$a$,与点$b(b_{x}<a_{x})$的斜率,大于新加入的点$i$与$b$的斜率,那么如果右侧出现一个点,向他们连直线,显然$i$的斜率大于$a$,可以用三角形的性质去证
因为$x$递增,用单调栈维护下凸包即可
时间$O(nlogn)$
貌似比较斜率必须用$double$,不然爆$long\;long$
1 #include <cmath> 2 #include <queue> 3 #include <vector> 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #define N1 101000 8 #define M1 205 9 #define ll long long 10 #define dd double 11 #define uint unsigned int 12 using namespace std; 13 14 ll gll() 15 { 16 ll ret=0;int fh=1;char c=getchar(); 17 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 18 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 19 return ret*fh; 20 } 21 int n; 22 ll D; 23 ll a[N1],s[N1],sa[N1]; 24 ll x[N1],y[N1]; 25 int stk[N1],tp; 26 inline dd gslope(int i,int j){ 27 return 1.0*(y[i]-y[j])/(x[i]-x[j]); 28 } 29 30 int main() 31 { 32 //freopen("t2.in","r",stdin); 33 scanf("%d%lld",&n,&D); 34 for(int i=1;i<=n;i++) 35 a[i]=gll(),s[i]=gll(),sa[i]=sa[i-1]+a[i]; 36 dd ans=0; 37 for(int i=1;i<=n;i++) 38 { 39 x[i]=1.0*i*D,y[i]=sa[i-1]; 40 while(tp>1&&gslope(stk[tp],stk[tp-1])>=gslope(i,stk[tp-1])) 41 tp--; 42 stk[++tp]=i; 43 int l=1,r=tp,mid1,mid2; 44 x[0]=1.0*s[i]+1.0*D*i,y[0]=sa[i]; 45 while(r-l>=3) 46 { 47 mid1=(l+l+r)/3,mid2=(l+r+r)/3; 48 if(gslope(0,stk[mid1])>gslope(0,stk[mid2])) 49 r=mid2; 50 else 51 l=mid1; 52 } 53 dd ma=0; 54 for(int j=l;j<=r;j++) 55 ma=max(ma,gslope(0,stk[j])); 56 ans+=ma; 57 } 58 printf("%.0lf\n",ans); 59 return 0; 60 }