[bzoj3203][Sdoi2013]保护出题人
人生第一道三分?。。。
把进攻序列里的前i只僵尸看成一个点,横坐标是第i只僵尸到达的时间,纵坐标是这i只僵尸的血量总和。。就是说植物必须在这段时间内输出这些伤害。。那么单位时间的输出伤害就是斜率了。
问题就变成了对于若干个点,求从原点到各个点斜率的最大值。
因为D是固定的,而每次新加入僵尸实际就是把原来的点平移。。并且相对位置关系不变。。。所以显然斜率最大的点一定在凸包上= =
每次就新加入一个点并维护凸包,然后三分找出凸包上的点与原点连线斜率的最大值(原点与凸包上各个点连线的斜率是单峰的)就好了。。
数据范围有点吓人。。。但事实上double就够了。。。判断浮点数大小的时候也不用写eps= =
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define d double 5 #define ll long long 6 using namespace std; 7 const int maxn=100233; 8 9 ll hp[maxn],tr[maxn]; 10 int dl[maxn],l,r; 11 ll n,m,D,dis; 12 int i,j,k; 13 d ans; 14 15 ll ra;char rx; 16 inline ll read(){ 17 rx=getchar(),ra=0; 18 while(rx<'0'||rx>'9')rx=getchar(); 19 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 20 } 21 22 inline void add(int x,ll v){for(;x<=n;x+=x&(-x))tr[x]+=v;} 23 inline ll query(int x){ 24 ll tmp=0; 25 for(;x;x-=x&(-x))tmp+=tr[x]; 26 return tmp; 27 } 28 inline d prexl(int x){ 29 return (d)query(x)/(d)(dis+D*(x-i)); 30 }//原点出发的斜率 31 inline d disxl(int a,int b){ 32 return (d)(query(b)-query(a))/(d)(D*(b-a)); 33 }//两点间斜率 34 inline d get(int l,int r){ 35 int l1,r1; 36 while(l+2<r){ 37 l1=l+(r-l+1)/3,r1=r-(l1-l); 38 if(prexl(dl[l1])<=prexl(dl[r1]))l=l1;else r=r1; 39 } 40 for(l1=l+1,r1=dl[l];l1<=r;l1++)if(prexl(dl[l1])>prexl(r1))r1=dl[l1]; 41 return prexl(r1); 42 } 43 44 int main(){ 45 n=read(),D=read(); 46 l=n+1,r=n; 47 for(i=n;i;i--){ 48 hp[i]=read(),add(i,hp[i]),dis=read();//printf(" %lld %lld\n",hp[i],dis); 49 // for(j=i;j<=n;j++)printf(" %lld",query(j));puts(""); 50 while(l<r&& disxl(i,dl[l])<=disxl(dl[l],dl[l+1]) )l++; 51 dl[--l]=i; 52 ans+=get(l,r); 53 } 54 printf("%.0lf\n",ans); 55 return 0; 56 }
写完这题B站排名就233啦