给定 \(n\) 个横坐标 \(x_i\) , 为它们选择一个不超过 \(y_i\) 的纵坐标 \(h_i\), 产生 \(c_ih_i\) 的花费. 选择以后产生的总价值是全部以 \((x_i,h_i)\) 到 \(x\) 轴的垂线段为斜边上的高的等腰直角三角形的并的面积. 最大化总价值与总花费之间的差并输出这个差.c++
\(n\le 1\times 10^5\).函数
首先有一个比较显然的沙雕性质, 就是某个三角形的高要么是 \(0\) 要么是 \(y_i\). 由于假设已经选择了一些三角形, 要求选择这个点以后对最后答案的贡献, 必然是一个下凸的二次函数. 显然这样的函数的区间最大值只能取在端点.this
其次有另外一个显然可是容易被忽略的性质, 就是最优解中不存在一个三角形被另外一个三角形彻底包含. 也就是说当计算选中一个新的三角形的贡献的时候不可能会出现一些鬼畜边界线的状况考试时成功忽略这个性质因而没敢写spa
由于不可能被彻底包含, 因此咱们能够按照三角形在 \(x\) 轴上覆盖的右端点升序排序, 定义 \(dp_i\) 表示前 \(i\) 个三角形中选中最后一个三角形时能产生的最大答案. 由于不难发现惟一须要出现的浮点数是 \(\frac 1 4\), 因而咱们计算答案的 \(4\) 倍, 这样就有三种转移状况.code
定义 \(l_i,r_i\) 分别为三角形在 \(x\) 轴上覆盖的左右端点, \(w_i\) 为 \(4(y_i^2-c_iy_i)\) :
\[ dp_i=\max_{j<i} \begin{cases} dp_j+w_i &(r_j<l_i)\\ dp_j-(r_j-l_i)^2+w_i &(r_j\ge l_i) \end{cases} \]
其中第一种转移显然是个前缀 \(\max\), 由于已经按 \(r_i\) 排过序了因此直接二分找到最后一个作这种贡献的位置而后取前缀 \(\max\) 就能够了.blog
第二种转移须要拆一拆...
\[ \begin{aligned} dp_i&=\max_{j<i,r_j\ge l_i} \{dp_j-(r_j-l_i)^2+w_i\}\\ &=\max_{j<i,r_j\ge l_i}\{dp_j-r_j^2+2r_jl_i-l_i^2+w_i\}\\ &=\max_{j<i,r_j\ge l_i}\{dp_j-r_j^2+2r_jl_i\}-l_i^2+w_i \end{aligned} \]
其中 \(\max\) 里面是一个一次函数的形式, 能够用李超树来搞.排序
等一下!get
李超树怎么处理 \(r_j\ge l_i\) 这个限制呢? 换句话说, 咱们怎么在计算第二部分贡献的时候排除 \(r_j<l_i\) 的点的贡献呢?it
其实不用排除由于若是某个点同时作贡献的话第二种贡献是要小于第一种的233class
因而直接李超树上就能够了. 总时间复杂度 \(O(n\log n)\).
#include <bits/stdc++.h> const int MAXN=1e5+10; typedef long long intEx; struct Point{ int l; int r; intEx w; }; Point P[MAXN]; int n; int s[MAXN]; intEx smax[MAXN]; struct Line{ intEx k; intEx b; Line(const intEx& a,const intEx& b):k(a),b(b){} inline intEx operator()(const int& x)const{ return k*s[x]+b; } }; struct Node{ int l; int r; Line f; Node* lch; Node* rch; ~Node(); Node(int,int); intEx Query(int); void Insert(Line); }; int main(){ freopen("radar.in","r",stdin); freopen("radar.out","w",stdout); int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x,y,c; scanf("%d%d%d",&x,&y,&c); P[i].l=x-y; P[i].r=x+y; s[i]=P[i].l; P[i].w=4ll*y*y-4ll*c*y; } std::sort(P+1,P+n+1, [](const Point& a,const Point& b){ return a.r<b.r; } ); std::sort(s+1,s+n+1); int cnt=std::unique(s+1,s+n+1)-s-1; Node* N=new Node(1,cnt); for(int i=1;i<=n;i++){ // printf("[%d,%d] w=%lld\n",P[i].l,P[i].r,P[i].w); int h=std::lower_bound(s+1,s+cnt+1,P[i].l)-s; intEx dp=N->Query(h)-1ll*P[i].l*P[i].l+P[i].w; int p=std::lower_bound(P+1,P+n+1,P[i].l, [](const Point& a,const int& b){ return a.r<b; } )-P-1; dp=std::max(smax[p]+P[i].w,dp); N->Insert(Line(2*P[i].r,dp-1ll*P[i].r*P[i].r)); smax[i]=std::max(smax[i-1],dp); } printf("%lld.%02lld\n",smax[n]>>2,(smax[n]&3)*25); delete N; } return 0; } intEx Node::Query(int x){ if(this->l==this->r) return this->f(x); else{ if(x<=this->lch->r) return std::max(this->lch->Query(x),this->f(x)); else return std::max(this->rch->Query(x),this->f(x)); } } void Node::Insert(Line f){ int mid=(this->l+this->r)>>1; if(f(mid)>this->f(mid)) std::swap(f,this->f); intEx ld=this->f(this->l)-f(this->l); intEx rd=this->f(this->r)-f(this->r); if(ld>=0&&rd>=0) return; else if(rd>=0) this->lch->Insert(f); else this->rch->Insert(f); } Node::~Node(){ if(this->lch) delete this->lch; if(this->rch) delete this->rch; } Node::Node(int l,int r):l(l),r(r),f(0,-1e18),lch(NULL),rch(NULL){ if(l!=r){ int mid=(l+r)>>1; this->lch=new Node(l,mid); this->rch=new Node(mid+1,r); } }