[YC703]ゴミ拾い Easy
题目大意:
二维平面内有\(n(n\le3\times10^5)\)个人和\(n\)个物品,第\(i\)个人在\((a_i,0)\)上,第\(i\)个物品在\((x_i,y_i)(0\le a_i,x_i,y_i\le10^5)\)上,满足\(a_i<a_{i+1},x_i<x_{i+1}\)。每个人可以取走一些物品或者一个也不取。一个人取走物品\(j\sim i\)的代价为这个人到物品\(j\)距离的平方。每个人不能取比自己编号大的物品,问所有物品都被取完的最小代价。
思路:
用\(f[i]\)表示前\(i\)个人取走前\(i\)个物品的最小代价,一个显然的DP为:\(f[i]=\min\limits_{0\le j<i}\{f[j]+(x_{j+1}-a_i)^2+y_{j+1}^2\}\)。
将\(\min\)中间的展开,就是\(-2x_{j+1}a_i+x_{j+1}^2+y_{j+1}^2+f[j]\)。可以看作是一个关于\(a_i\)的一次函数。使用李超树维护一次函数最小值即可。
时间复杂度\(\mathcal O(n\log n)\)。
源代码:
#include#include #include #include inline int getint() { register char ch; while(!isdigit(ch=getchar())); register int x=ch^'0'; while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); return x;}using int64=long long;const int N=3e5+1,LIM=1e5;int a[N],x[N],y[N];int64 f[N];using Line=std::pair ;class SegmentTree { #define _left <<1 #define _right <<1|1 #define mid ((b+e)>>1) private: Line node[LIM<<2]; int64 calc(const Line &l,const int &x) const { return l.first*x+l.second; } public: void build(const int &p,const int &b,const int &e) { node[p]={0,LLONG_MAX}; if(b==e) return; build(p _left,b,mid); build(p _right,mid+1,e); } void insert(const int &p,const int &b,const int &e,Line l1) { if(node[p].second==LLONG_MAX) { node[p]=l1; return; } Line l2=node[p]; if(calc(l2,b)>=calc(l1,b)) std::swap(l1,l2); if(calc(l1,e)>=calc(l2,e)) { node[p]=l2; return; } if(b==e) return; const double c=1.*(l2.second-l1.second)/(l1.first-l2.first); if(c<=mid) { node[p]=l1; insert(p _left,b,mid,l2); } else { node[p]=l2; insert(p _right,mid+1,e,l1); } } int64 query(const int &p,const int &b,const int &e,const int &x) const { int64 ret=calc(node[p],x); if(b==e) return ret; if(x<=mid) ret=std::min(ret,query(p _left,b,mid,x)); if(x>mid) ret=std::min(ret,query(p _right,mid+1,e,x)); return ret; } #undef _left #undef _right #undef mid};SegmentTree t;int main() { const int n=getint(); for(register int i=1;i<=n;i++) a[i]=getint(); for(register int i=1;i<=n;i++) x[i]=getint(); for(register int i=1;i<=n;i++) y[i]=getint(); t.build(1,1,LIM); for(register int i=1;i<=n;i++) { t.insert(1,1,LIM,(Line){-2*x[i],(int64)x[i]*x[i]+(int64)y[i]*y[i]+f[i-1]}); f[i]=t.query(1,1,LIM,a[i])+(int64)a[i]*a[i]; } printf("%lld\n",f[n]); return 0;}