[省选联考 2025] 推箱子

我们先考虑 做法。按照 从小到大排序后,依次满足每个箱子的目标需求。若箱子移动的路线被其他箱子阻挡,此箱子会推着阻挡物前进。在此期间,任何一个箱子无法在规定时间内达到目标,将输出 No,否则为 Yes

按照上述过程进行模拟,处理每个箱子时,若发现其前面 / 后面的箱子阻挡了其行进路线,将其直接移动到目标的前 / 后一格与上述描述也是等价的。若阻挡物也被阻挡了,进行同样过程的处理即可。

正解便是在此基础上进行的优化。我们发现,箱子推到一起后,会和其他箱子粘在一起运动许久。具体地,当一个箱子与后面的箱子粘连后,仅在后方箱子前往目标或自己前往目标时可能分离,且不会再次粘连,因此每两个箱子之间粘连 / 分开的次数是 的。

珂朵莉树 可以维护此过程 我怎么以前不知道这个东西是有名字的。我们维护一个 map,键存放 段首元素的下标,值维护 段的长度段首元素的位置 的值)。由箱子相连可知,除段首元素,段内每个箱子的位置 都是前一个的位置 ,因此可以维护 数组。每次处理一个箱子时,先将该箱子单独分裂出来,然后维护出每个块前一个 / 后一个空着的位置 ,共其他段计算需要移动的距离。接着,处理路径中遇到的段,将其删除并记录,稍后进行合并操作,同时还要维护出新的 。等清理完所有障碍,移动到最终位置后,便可将合并出的新块添加回 map 中了。

按照上述分析,因此每两个箱子之间粘连 / 分开的次数是 的,算上 map 单次操作的复杂度为 ,最后的总复杂度为 ,且相比线段树做法,实现相当简单。

#include<bits/stdc++.h>
using namespace std;
using ui=unsigned int;
using uli=unsigned long long int;
istream& fin=cin;
ostream& fout=cout;
int main(void){
  ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
  ui c;size_t T;fin>>c>>T;
  while (T--){
    size_t n;fin>>n;
    map<ui,pair<ui,ui>> a;
    vector<ui> b(n);
    vector<pair<uli,size_t>> t(n);
    for (size_t i=0;i<n;++i){
      ui x;
      fin>>x>>b[i]>>t[i].first,t[i].second=i;
      a.insert({i,{1,x}});
    }
    auto blkFst=[](decltype(a)::iterator x)->auto&{return x->first;};
    auto blkPos=[](decltype(a)::iterator x)->auto&{return x->second.second;};
    auto blkLen=[](decltype(a)::iterator x)->auto&{return x->second.first;};
    sort(t.begin(),t.end());
    uli d=0;
    for (auto const& v:t){
      size_t p=v.second;
      auto it=prev(a.upper_bound(p));
      if (blkFst(it)<p){
        a.insert({p,{blkFst(it)+blkLen(it)-p,blkPos(it)+(p-blkFst(it))}});
        it->second.first=p-blkFst(it);
        ++it;
      }
      if (blkLen(it)>1){
        a.insert({p+1,{blkLen(it)-1,blkPos(it)+1}});
        blkLen(it)=1;
      }
      if (blkPos(it)<b[p]){
        ui e=b[p];
        do{
          d+=uli(e-blkPos(it))*blkLen(it);
          e+=blkLen(it);
          it=a.erase(it);
        }while (it!=a.end()&&blkPos(it)<e);
        a.insert({p,{e-b[p],b[p]}});
      }else if (blkPos(it)>b[p]){
        ui e=b[p]+1;
        do{
          d+=uli(blkPos(it)+blkLen(it)-e)*blkLen(it);
          e-=blkLen(it);
          it=a.erase(it);
          --it;
        }while (it!=a.end()&&blkPos(it)+blkLen(it)>e);
        a.insert({p+1-(b[p]+1-e),{b[p]+1-e,e}});
      }
      if (d>v.first){fout<<"No\n";goto end1;}
    }
    fout<<"Yes\n";
    end1:;
  }
}
posted @ 2025-03-02 19:03  MrPython  阅读(7)  评论(0)    收藏  举报  来源