[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 }
View Code

 

写完这题B站排名就233啦

你可能感兴趣的:([bzoj3203][Sdoi2013]保护出题人)