题解 下楼
Find_Yourself · · 题解
不难推理出当初始绳长为
当
正文:
线段树优化 DP。
我们先将所有钢管按高度从大到小排序,如果高度相同则按权值从小到大排序。
然后将权值离散化,对于相同的
考虑从低往高 DP。
定义
转移方程如下:
开两棵线段树,第一棵
容易发现,一根钢管的贡献是有范围的。
对于钢管
具体的,我们假设二分到的位置为
这里之所以可以通过赋值
注意,由于
如果你还不放心,可以在每次除以
我们之所以不考虑正着推(从上往下),是因为多一个二分带来的
综上,复杂度
code:
#include<bits/stdc++.h>
#define ls (id*2)
#define rs (id*2+1)
using namespace std;
typedef long long ll;
const ll N=5e5+5,inf=LLONG_MAX>>2;
ll n,f[N],p;
vector<int>vec[N];
struct node{ll h,v;}a[N];
bool cmph(node x,node y){return x.h!=y.h?x.h>y.h:x.v<y.v;}
bool cmpv(node x,node y){return x.v!=y.v?x.v<y.v:x.h>y.h;}
struct tree{
ll mi[4*N];
void change(int id,int l,int r,int x,ll k){
if(l==r){mi[id]=k;return;}
int mid=(l+r)>>1;
if(x<=mid)change(ls,l,mid,x,k);
else change(rs,mid+1,r,x,k);
mi[id]=min(mi[ls],mi[rs]);
}
ll query(int id,int l,int r,int L,int R){
if(l==L&&r==R)return mi[id];
int mid=(l+r)>>1;
if(R<=mid)return query(ls,l,mid,L,R);
else if(L>mid)return query(rs,mid+1,r,L,R);
else return min(query(ls,l,mid,L,mid),query(rs,mid+1,r,mid+1,R));
}
}t,t2;
inline void get(int id){
int l=1,r=id,ans;
while(l<=r){
int mid=(l+r)>>1;
if(a[mid].h<=a[id].h+(f[id]>>1))ans=mid,r=mid-1;
else l=mid+1;
}
t.change(1,1,p,a[id].v,f[id]);
vec[ans-1].push_back(id);
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;p=n;
for(int i=1;i<=n;++i)cin>>a[i].h>>a[i].v;
sort(a+1,a+n+1,cmpv);
for(int i=1;i<=n;++i)a[i].v=i;
sort(a+1,a+n+1,cmph);a[n+1].v=++p;
for(int i=1;i<=4*p;++i)t.mi[i]=t2.mi[i]=inf;
get(n+1);
for(int i=n;i>=1;--i){
for(int j=0;j<vec[i].size();++j){
int id=vec[i][j];
t.change(1,1,p,a[id].v,inf);
t2.change(1,1,p,a[id].v,-a[id].h+((f[id]+1)>>1));
}
f[i]=inf;
f[i]=min(f[i],min(t.query(1,1,p,a[i].v+1,p),t2.query(1,1,p,a[i].v+1,p)+a[i].h));
get(i);
}
cout<<f[1]<<endl;
return 0;
}