1. 程式人生 > >BZOJ 3203 [SDOI2013]保護出題人 (凸包+三分)

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 }