給定 \(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); } }