1 条题解

  • 0
    @ 2025-8-24 23:12:35

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar FFTotoro
    龙猫

    搬运于2025-08-24 23:12:35,当前版本为作者最后更新于2025-03-08 17:59:53,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Dreaming is not harmful.

    有一个很重要的点:如果不考虑降低员工能力值,那么所有员工被开除的顺序是固定的。先处理出这个序列(可以使用优先队列维护这个过程),记 ord(i)\mathrm{ord}(i) 为第 ii 天当上 CEO 的员工编号。

    接着观察到将一个员工的能力值降为 00,等价于删掉这个员工和他 / 她的所有下属(即忽略一棵子树)。假设员工 uu 满足 ord(p)=u\mathrm{ord}(p)=u,那么只有员工 $\mathrm{ord}(1),\mathrm{ord}(2),\ldots,\mathrm{ord}(p-1)$ 可能影响到他 / 她。只需要选择一棵子树(当然这棵子树不能包含 uu),使得其中包含尽可能多的上述员工,进行删除后的答案就是最优的。

    维护一棵带权树 TT:顺次求解 $\mathrm{ord}(1),\mathrm{ord}(2),\ldots,\mathrm{ord}(n)$ 的答案,设当前考虑到的员工编号为 uu,那么每次求解结束后就将 TTuu 到根的路径上所有点权值 +1+1;于是查询最大子树和变为单点权值,就可以用树链剖分 + 线段树维护。

    注意特别处理“子树不能包含 uu”的限制:考虑一个极大值 II(可以取 I=1018I=10^{18}),只需要在求解前将 uu 到根的链上点的权值都减去 II,求解后再加上 II 即可。

    时间复杂度 O(nlog2n)O(n\log^2n)

    代码中为了方便实现,直接将员工编号替换为其能力值。放代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    const int I=1e9;
    class segtree{
      private:
        vector<pii> B;
        vector<ll> S,T;
        inline void pushup(int u){
          S[u]=max(S[u<<1],S[u<<1|1]);
        }
        inline void pushdown(int u){
          if(T[u]){
            S[u<<1]+=T[u],S[u<<1|1]+=T[u];
            T[u<<1]+=T[u],T[u<<1|1]+=T[u],T[u]=0;
          }
        }
      public:
        segtree(int n):B(n<<2),S(n<<2),T(n<<2){
          function<void(int,int,int)> build=[&](int u,int l,int r){
            if(B[u]=make_pair(l,r);l==r)return;
            int m=l+r>>1;
            build(u<<1,l,m),build(u<<1|1,m+1,r);
          };
          build(1,0,n-1);
        }
        void update(int u,int l,int r,int d){
          if(B[u].first>r||B[u].second<l)return;
          if(l<=B[u].first&&B[u].second<=r){
            S[u]+=d,T[u]+=d; return;
          }
          pushdown(u);
          update(u<<1,l,r,d),update(u<<1|1,l,r,d);
          pushup(u);
        }
        inline int all_prod(){return S[1];}
    }; // 线段树
    class hld{
      private:
        int n;
        vector<int> f,e,h,t,l;
        segtree T;
      public:
        hld(int rt,vector<vector<int> > g):n(g.size()),T(n){
          f.resize(n),e.resize(n,1);
          h.resize(n,-1),t.resize(n),l.resize(n);
          function<void(int)> dfs=[&](int u){
            int mx=0;
            for(int i:g[u]){
              f[i]=u,dfs(i),e[u]+=e[i];
              if(e[i]>mx)mx=e[i],h[u]=i;
            }
          };
          int o=0;
          function<void(int,bool)> decomp=[&](int u,bool b){
            t[u]=b?t[f[u]]:u,l[u]=o++;
            if(~h[u])decomp(h[u],true);
            for(int i:g[u])
              if(i!=h[u])decomp(i,false);
          };
          f[rt]=-1,dfs(rt),decomp(rt,false);
        }
        inline void update(int u,int d){
          while(~u)T.update(1,l[t[u]],l[u],d),u=f[t[u]];
        } // 链加
        inline int query(){return T.all_prod();} // 全局 max
    }; // 树链剖分
    int main(){
      ios::sync_with_stdio(false);
      int n,m; cin>>n>>m;
      vector<pii> e;
      for(int i=1;i<n;i++){
        int f; cin>>f;
        e.emplace_back(f-1,i);
      }
      vector<int> s(n),o,r(n);
      for(auto &i:s)cin>>i,i--;
      vector<vector<int> > g(n);
      for(auto [u,v]:e)
        g[s[u]].emplace_back(s[v]);
      priority_queue<int> q;
      q.emplace(s[0]);
      while(!q.empty()){
        int u=q.top(); q.pop();
        o.emplace_back(u);
        for(int i:g[u])q.emplace(i);
      } // 求解原顺序
      hld h(s[0],g);
      for(int i=0;i<n;i++){
        int u=o[i];
        h.update(u,-I);
        r[u]=i-max(0,h.query());
        h.update(u,I+1);
      } // 依次求解
      while(m--){
        int x; cin>>x;
        cout<<r[s[x-1]]<<' ';
      }
      cout<<endl;
      return 0;
    }
    
    • 1

    信息

    ID
    11929
    时间
    3000ms
    内存
    256MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者