P3103 [USACO14FEB] Airplane Boarding G 做题记录

P3103 [USACO14FEB] Airplane Boarding G

Description

https://www.luogu.com.cn/problem/P3103

Solution

考虑将奶牛的移动刻画成折线。

每一只奶牛的折线都不能与之前的奶牛重合。

观察一下,斜向上的折线不会对后面的奶牛造成任何影响,所以只需要保留竖直的折线。

我们把这些竖直的折线抽象为一个二元组 \((s_j,t_j)\),表示时刻 \(t_j\) 之后才可以到达 \(s_j\)

那么奶牛 \(i\) 到达终点 \(x_i\) 的时间 \(v_i\) 即为 \(\max\{x_i-s_j+t_j\}\),即 \(x_i-\max\{t_j-s_j\}\)。于是只需要维护一段前缀的 \(t_j-s_j\) 的最大值。到达终点后,会增加一条 \((x_i,v_i+1)\) 的竖直折线。

观察折线图,奶牛 \(i\) 经过 \((s_j,t_j)\) 这条线后,会新增一个 \((s_j-1,t_j)\) 的竖直折线。相当于对一段前缀的 \(s_i\) 全部减一。

需要支持单点插入、区间加、区间 \(\max\),平衡树维护。时间复杂度 \(O(n\log n)\)

int n,s[N],t[N];

mt19937 rnd(time(0));

struct FHQ_Treap{
    int lc,rc,pri;
    int key,val,mx,add;
}tr[N];

int tot,root;

int NewNode(int x,int y){
    tr[++tot]={0,0,(signed)rnd(),x,y,y,0};
    return tot;
}

void Add(int p,int v){
    tr[p].key-=v;
    tr[p].val+=v;
    tr[p].mx+=v;
    tr[p].add+=v;
}

void Spread(int p){
    if(!tr[p].add) return;
    if(tr[p].lc) Add(tr[p].lc,tr[p].add);
    if(tr[p].rc) Add(tr[p].rc,tr[p].add);
    tr[p].add=0;
}

void Pushup(int p){
    tr[p].mx=max({tr[p].val,tr[tr[p].lc].mx,tr[tr[p].rc].mx});
}

void Split(int p,int v,int &L,int &R){
    if(!p) return L=R=0,void();
    Spread(p);
    if(tr[p].key<=v) L=p,Split(tr[p].rc,v,tr[p].rc,R);
    else R=p,Split(tr[p].lc,v,L,tr[p].lc);
    Pushup(p);
}

int Merge(int p,int q){
    if(!p) return q;
    if(!q) return p;
    if(tr[p].pri>tr[q].pri){
        Spread(p);
        tr[p].rc=Merge(tr[p].rc,q);
        return Pushup(p),p;
    }
    else{
        Spread(q);
        tr[q].lc=Merge(p,tr[q].lc);
        return Pushup(q),q;
    }
}

int Ask(int v){
    int L,R,res;
    Split(root,v,L,R);
    res=tr[L].mx;
    root=Merge(L,R);
    return res;
}

void Insert(int x,int y){
    int L,R,M=NewNode(x,y);
    Split(root,x,L,R);
    root=Merge(Merge(L,M),R);
}

void Update(int v){
    int L,R;
    Split(root,v,L,R);
    Add(L,1);
    root=Merge(L,R);
}

signed main(){
    read(n);
    for(int i=1;i<=n;i++) read(s[i]),read(t[i]);
    reverse(s+1,s+n+1);
    reverse(t+1,t+n+1);
    int ans=0;
    for(int i=1;i<=n;i++){
        int res=max(s[i]+t[i]+i-1,s[i]+t[i]+Ask(s[i]));
        Ckmax(ans,res);
        Update(s[i]);
       Insert(s[i],res+1-s[i]);
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2025-07-16 19:17  XP3301_Pipi  阅读(17)  评论(0)    收藏  举报
Title