Loading

题解:P11833 [省选联考 2025] 推箱子

P11883

一开始点开讨论区看到一哥们说什么权值线段树,我就直接开始想维护每个时间点被谁用了,想想这真是个笑话,于是看了题解。
首先考虑按照 \(t\) 从小到大依次移动,如果路上遇到挡道的,那就直接粘连起来一并往前推,这个显然正确的。
然后你总得考虑维护什么东西,你考虑维护每一个箱子的目前的位置相关的信息,我们把目前的位置记作 \(f_i\)。然后你维护这玩意的目的其实就是快速返回一个箱子挪到目的地所需要的时间嘛,那你考虑一下箱子挪动的情况。进行一个箱子的挪动不是可能会波及到它路程上的其它箱子吗,这太麻烦了于是我们总体考虑:如果箱子往左挪,那么就是初始的位置和减去末尾的位置和,反过来就是末尾减初始。
然后考虑一个箱子 \(j\) 何时会被一个向左挪的 \(i\) 带动,显然是 \(b_i - (i - j - 1) \le f_j\) 的情况。我们把 \(i\)\(j\) 分别放到两边,可得 \(b_i - i + 1 \le f_j - j\)。然后我们注意力惊人地发现每次被带动的箱子其实是连续的一段,这启发我们用二分来找最后一个满足该条件的箱子 \(j\),然后 \(f_j - j\) 其实又是单调的,所以直接线段树上二分即可,但是要写两个(。这个时候,我们其实就可以考虑维护 \(f_i - i\) 了,那么注意到这个 \(i\) 其实是不变的,那么直接用这个做差求移动时间是正确的。那么考虑移动之后每一个 \(f_i - i\) 会变成什么,显然是变成和这一粘连段最左边的 \(f_i - i\) 一样的值。向右挪同理,那么只需要维护一个支持区间推平,线段树二分,总体求和的线段树即可。\(T \le 6\),你比幸运数字出题人要好。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+7;
int n,a[N],b[N],t[N],ord[N];
struct SGT{
    #define ls (x<<1)
    #define rs ((x<<1)|1)
    #define mid ((l+r)>>1)
    int v[N<<2],lz[N<<2],mn[N<<2],mx[N<<2];
    void pu(int x){
        v[x]=v[ls]+v[rs];
        mn[x]=min(mn[ls],mn[rs]);
        mx[x]=max(mx[ls],mx[rs]);
    }
    void build(int x,int l,int r){
        lz[x]=-1; if(l==r) return v[x]=mx[x]=mn[x]=a[l]-l,void();
        build(ls,l,mid),build(rs,mid+1,r),pu(x);
    }
    void pd(int x,int l,int r){
        if(lz[x]==-1) return;
        v[ls]=(mid-l+1)*lz[x],v[rs]=(r-mid)*lz[x];
        mn[ls]=mn[rs]=mx[ls]=mx[rs]=lz[x];
        lz[ls]=lz[rs]=lz[x]; lz[x]=-1;
    }
    int whsum(){return v[1];}
    void mdf(int x,int l,int r,int ql,int qr,int k){
        if(ql<=l&&r<=qr){
            mn[x]=mx[x]=lz[x]=k;
            v[x]=(r-l+1)*k; return;
        }
        pd(x,l,r);
        if(ql<=mid) mdf(ls,l,mid,ql,qr,k);
        if(mid<qr)  mdf(rs,mid+1,r,ql,qr,k);
        pu(x);
    }
    int fdl(int x,int l,int r,int k){
        if(l==r) return l;
        if(mx[ls]>=k) return fdl(ls,l,mid,k);
        return fdl(rs,mid+1,r,k);
    }
    int fdr(int x,int l,int r,int k){
        if(l==r) return l;
        if(mn[rs]<=k) return fdr(rs,mid+1,r,k);
        return fdr(ls,l,mid,k);
    }
    int pos(int x,int l,int r,int q){
        if(l==r) return v[x]+l;
        if(q<=mid) return pos(ls,l,mid,q);
        else return pos(rs,mid+1,r,q);
    }
} Misaka;
bool cmp(int x,int y){return t[x]<t[y];};
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i]>>b[i]>>t[i],ord[i]=i;
    sort(ord+1,ord+n+1,cmp);
    Misaka.build(1,1,n); int cur=0;
    for(int j=1,i=ord[j];j<=n;j++,i=ord[j]){
        int f=Misaka.pos(1,1,n,i);
        int pre=Misaka.whsum();
        if(b[i]<f){
            int lb=Misaka.fdl(1,1,n,b[i]-i+1);
            Misaka.mdf(1,1,n,lb,i,b[i]-i);
            cur+=pre-Misaka.whsum();
        }
        else if(b[i]>f){
            int rb=Misaka.fdr(1,1,n,b[i]-i-1);
            Misaka.mdf(1,1,n,i,rb,b[i]-i);
            cur+=Misaka.whsum()-pre;
        }
        if(cur>t[i]) return cout<<"No\n",void();
    }
    cout<<"Yes\n";
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int c,t; cin>>c>>t;
    while(t--) solve();
    return 0;
}
posted @ 2026-03-02 09:34  GE9x  阅读(16)  评论(0)    收藏  举报