【JZOJ3299】【SDOI2013】保护出题人 三分+凸壳

题面

​出题人铭铭认为给SDOI2012 出题太可怕了,因为总要被骂,于是他又给SDOI2013 出题了。

参加SDOI2012 的小朋友们释放出大量的僵尸,企图攻击铭铭的家。而你作为SDOI2013的参赛者,你需要保护出题人铭铭。

僵尸从唯一一条笔直道路接近,你们需要在铭铭的房门前放置植物攻击僵尸,避免僵尸碰到房子。第一关,一只血量为a1 点的僵尸从距离房子x1 米处匀速接近,你们放置了攻击力为y1 点/秒的植物进行防御;第二关,在上一关基础上,僵尸队列排头增加一只血量为a2点的僵尸,与后一只僵尸距离d 米,从距离房子x2 米处匀速接近,你们重新放置攻击力为y2 点/秒的植物;……;第n 关,僵尸队列共有n 只僵尸,相邻两只僵尸距离d 米,排头

僵尸血量为an 点,排第二的僵尸血量a_n −1 ,以此类推,排头僵尸从距离房子xn 米处匀速接近,其余僵尸跟随排头同时接近,你们重新放置攻击力为yn 点/秒的植物。

每只僵尸直线移动速度均为1 米/秒,由于植物射击速度远大于僵尸移动速度,可忽略植物子弹在空中的时间。所有僵尸同时出现并接近,因此当一只僵尸死亡后,下一只僵尸立刻开始受到植物子弹的伤害。

游戏得分取决于你们放置的植物攻击力的总和Σyi (1<=i<=n),和越小分数越高,为了追求分数上界,你们每关都要放置攻击力尽量小的植物。

作为SDOI2013 的参赛选手,你们能保护出题人么?
对于100%的数据, 1≤n≤10^5,1≤d≤10^12 ,1≤x≤10^12 ,1≤a≤10^12 。

抽象题意

(f_i=sum_{j=1}^ia_j)
显然$$
ans_i=max_{j=1}^ifrac{f_i-f_{j-1}}{x_i+di-dj}

[于是就有个$O(n^2)$的做法了; 然后是可以过50%。 #**100** 我们把$frac{f_i-f_{j-1}}{x_i+d*i-d*j}$看做是,点$(x_i+d*i,f_i)$与$(d*j,f_{j-1})$的斜率; 那么我们就考虑维护这些斜率的最大值。 由于$x_i+d*i>d*j,f_i>f_{j-1}$,所以我们可以给$(d*j,f_{j-1})$维护一个下凸壳,然后三分找峰值即可。 #**Code** ``` #include<bits/stdc++.h> #define ll long long #define db double #define fo(i,x,y) for(int i=x;i<=y;i++) #define fd(i,x,y) for(int i=x;i>=y;i--) using namespace std; const char* fin="ex3299.in"; const char* fout="ex3299.out"; const int inf=0x7fffffff; const int maxn=100006; int n,b[maxn],hd,tl; db m; db a[maxn],f[maxn],x[maxn],ans; db count(int a,int b){return (f[a]-f[b-1])/(x[a]+m*a-m*b);} db xie(int a,int b){return (f[b-1]-f[a-1])/(m*(b-a));} int main(){ scanf("%d%lf",&n,&m); fo(i,1,n){ scanf("%lf%lf",&a[i],&x[i]); f[i]=f[i-1]+a[i]; } hd=1; tl=1; fo(i,1,n){ while (hd<tl && xie(b[tl],i)<xie(b[tl-1],i)) tl--; b[++tl]=i; int l=hd,r=tl; db tmp=0; while (l<r-2){ int mid=(l+r)/2,mmid=(mid+r+1)/2; if (count(i,b[mid])<count(i,b[mmid])) l=mid; else r=mmid; } for(;l<=r;l++) tmp=max(tmp,count(i,b[l])); ans+=tmp; } printf("%.0lf",ans); return 0; } ```]

原文地址:https://www.cnblogs.com/hiweibolu/p/6894922.html