BZOJ3203: [Sdoi2013]保护出题人【单调栈+二分+凸壳】

【吐槽:我不想保护出题人。。。】

首先把读入的a数组变成前缀和。然后就可以发现y数组其实就是这个东西这就是确保杀死j~i(j≤i)这一段僵尸的最小速度。伤害的和是a[i]-a[j-1],第j个~第i个之间的距离是di-dj,还要加上x[i]才是与家的距离。很绕吗?自己脑补吧。。

观察这个式子比较像求直线的斜率。那么我们把它转化为两点之间的斜率好了。把跟i有关的看成一个点,跟j有关的看成一个点。

那么就是(x[i]+d*i,a[i])(d*j,a[j-1])两个点。然后只要求斜率了!!!

那么用单调栈维护一个凸壳就好了!!!计算斜率的时候二分。二分的时候先判是不是在凸壳的外部,然后取栈里面最上面那个就好了。

很好。nlogn了。

这是样例,有线连着的点是转化过的(d*j,a[j-1])。其中虚线是最后形成的凸壳。

查询第i次的时候就是先转化为(x[i]+d*i,a[i])这个点,然后二分凸壳上的点求最大的斜率就好。

比如查询第一次。凸壳(黄线)中只有(2,0)这个点。要查询的是(5,3)。很明显斜率只能是1。

查询第二次。查询的点是(5,4)。凸壳中有两个点(2,0),(4,3)。选前者的算出的斜率1.3333比后者的1要优。

然后以此类推就可以解决全部询问。

这道题在八中上rank1了我会告诉你们?介于是rank1我就不发rank1的代码了、、这个是目前rank10的、、





#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef double D; D a[100005],x[100005],d,An=0;
struct P{D x,y;P(){};P(D _x,D _y):x(_x),y(_y){};}p[100005];
D cross(P a,P b,P c){ return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);}
int st[100005],top=0,n;
int main(){
    scanf("%d%lf",&n,&d);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&a[i],&x[i]);
        a[i]+=(p[i].y=a[i-1]);  p[i].x=i*d;
    }for(int i=1;i<=n;i++){
        while(top&&cross(p[st[top]],p[i],p[st[top-1]])<0)top--;
        st[++top]=i;     P s(x[i]+i*d,a[i]);
        int j=1,l=1,r=top,mid;  while(l<=r){
            mid=l+r>>1;
            if(cross(p[st[mid]],p[st[mid-1]],s)<0)j=max(j,mid),l=mid+1;else r=mid-1;
        }An+=(s.y-p[st[j]].y)/(s.x-p[st[j]].x);
    }printf("%.0lf\n",An);  return 0;
}