「NnOI R1-T4」下楼 解题报告
P9415 「NnOI R1-T4」下楼 题解
初步分析
首先要想绳子怎么用最优。因为环的部分可回收,因此环越长越好。若我们令绳子的最短长度为 \(L_{min}\),环的部分长 \(len\),该次下楼两管间隔为 \(dis\),当环长和楼层间隔确定时显然有:
算法分析
那么我们看本题,很容易想到dp,因此考虑怎么设计状态(下面所述管子编号默认为按高度升序排序后的编号)。
首先我们当然可以设 \(f_i\) 表示从最高的管子到 \(i\) 号管子的最小绳长,那我们发现:正如我们在初步分析处列出的式子,光知道楼层的间距是不能求出所需绳长的,我们还需要知道环长,但是环长在这个状态设计下找不到,因此我们列不出转移方程。
但是我们换一个方向思考,如果我们设 \(f_i\) 表示从 \(i\) 号管子到地面的最小绳长,那么 \(f_i\) 一定是从上一次下降时剩下的,也就是环长,那么我们就可以列出式子:
需要说明的是:上述式子中,\(0\) 号点是地面;并且,由于从上往下时权值不减,所以从下往上时权值不加。
直接这么推是 \(O(n^2)\) 的,需要优化dp过程。考虑分类讨论 \(f_i\) 的转移,即:
让我们关注当 \(f_i \le 2 \times (h_i-h_j)\) 的情况,这个式子可以改写为:
注意到括号内和小于等于号左边的式子都只和 \(j\) 有关,因此我们考虑使用线段树维护。具体地,每个节点分别维护当前区间内,两种范围内的最小值(此处范围指分类讨论 \(f_i\) 递推时,\(f_j\) 的取值范围)。
为了刻画权值不加的限制条件,我们可以将 \(i\) 点的信息存入 \(v_i\) 位置,在询问时询问 \([v_i,n]\) 区间,这样就保证传递到自己的状态,其权值一定不小于自己的。
因为每个点从第一个取值范围到第二个取值范围的临界点只跟自身有关,所以二分找第一个高度大于等于 \(f_i+ 2 \times h_i\) 的点,当递推到该点时,维护 \(i\) 号点的权值。
我们维护 \(i\) 号点的权值时,需要删除它在第一个取值范围内的值,并加入它的第二个取值范围内的值。删除操作可以通过优先队列和标记数组实现,其中标记数组记录每个点是否以及进入到第二个取值范围。
Talk is cheap,show me the code
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;i++)
#define Down(i,s,t) for(int i=s;i>=t;i--)
#define ls (i<<1)
#define rs (i<<1|1)
using namespace std;
typedef long long ll;
typedef pair<ll,int> pii;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
register int x=0;
char c=getchar();
while(c<'0' || '9'<c) c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
void write(ll x){
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int N=5e5+100;
int n,m;
vector<int> limit[N];
ll f[N],rk[N],list_h[N];
struct Node{ll h,v;}a[N];
bool cmp(Node x,Node y){return x.h<y.h;}
bool vis[N];//记录i号元素是否满足了 f[i]+2*h[i]<=h[j]
struct Tree{int l,r;ll val1,val2;}t[N<<2];
void push_up(int i){
t[i].val1=min(t[ls].val1,t[rs].val1);
t[i].val2=min(t[ls].val2,t[rs].val2);
}
void build(int i,int l,int r){
t[i].l=l,t[i].r=r;
if(l==r){
t[i].val1=t[i].val2=Inf;
return;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(i);
}
priority_queue<pii,vector<pii>,greater<pii> >q[N];//first是具体的值,second是编号
void update1(int i,int x,int id){
if(t[i].l==t[i].r){
q[x].push(make_pair(f[id],id));
t[i].val1=q[x].top().first;
return;
}
int mid=t[i].l+t[i].r>>1;
if(x<=mid)
update1(ls,x,id);
else
update1(rs,x,id);
push_up(i);
}
void update2(int i,int x,int id){
if(t[i].l==t[i].r){
while(!q[x].empty() && vis[q[x].top().second]) q[x].pop();
t[i].val1=(!q[x].empty() ? q[x].top().first : Inf);
t[i].val2=min(t[i].val2,(f[id]+1)/2-a[id].h);
return;
}
int mid=t[i].l+t[i].r>>1;
if(x<=mid)
update2(ls,x,id);
else
update2(rs,x,id);
push_up(i);
}
ll query1(int i,int l,int r){
if(l<=t[i].l && t[i].r<=r)
return t[i].val1;
int mid=t[i].l+t[i].r>>1;
if(l>mid) return query1(rs,l,r);
if(mid>=r) return query1(ls,l,r);
return min(query1(ls,l,r),query1(rs,l,r));
}
ll query2(int i,int l,int r){
if(l<=t[i].l && t[i].r<=r)
return t[i].val2;
int mid=t[i].l+t[i].r>>1;
if(l>mid) return query2(rs,l,r);
if(mid>=r) return query2(ls,l,r);
return min(query2(ls,l,r),query2(rs,l,r));
}
void init(){
For(i,1,n) rk[i]=a[i].v;
sort(rk+1,rk+n+1,less<ll>());
sort(a+1,a+n+1,cmp);
m=unique(rk+1,rk+n+1)-rk-1;
rk[++m]=Inf;
For(i,1,n) a[i].v=lower_bound(rk+1,rk+m+1,a[i].v)-rk;
build(1,1,m);
update2(1,m,0);//把直接落地的代价设为h[i]
For(i,1,n) list_h[i]=2*a[i].h;
sort(list_h+1,list_h+n+1,less<ll>());
list_h[n+1]=Inf;
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
n=read();
For(i,1,n) scanf("%lld%lld",&a[i].h,&a[i].v);
init();
int lim=0;
ll mn1,mn2,ans=Inf;
For(i,1,n){
//维护dp的两段
int sz=limit[i].size()-1;
For(j,0,sz) vis[limit[i][j]]=true,update2(1,a[limit[i][j]].v,limit[i][j]);
//dp
mn1=query1(1,a[i].v,m),mn2=query2(1,a[i].v,m)+a[i].h;
f[i]=min(mn1,mn2);
//向后维护
update1(1,a[i].v,i);
lim=lower_bound(list_h+1,list_h+n+2,f[i]+2*a[i].h)-list_h;
limit[lim].push_back(i);
}
Down(i,n,1){
ans=min(ans,f[i]);
if(a[i].h!=a[i-1].h) break;
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号