1 条题解

  • 0
    @ 2026-5-4 17:49:19

    一年半之前的模拟赛原题。

    从部分分 m=n1m=n-1 出发,问题转换为确定一个长度为 mm 的排列 w1,w2,,wmw_1,w_2,\ldots,w_m,满足 wi[li,ri]w_i\in[l_i,r_i]。这是一个非常经典的贪心:考虑依次确定值为 1,2,,m1,2,\ldots,m 的位置,每次取出 lil_i 最小的 ii,若有多个 ii 满足条件取 rir_i 最小的,令 wiw_i 为当前的值。这个过程可以优先队列维护;若过程中出现了 wiw_i 不满足条件的情况,那么问题必然无解。

    现在考虑如何使前 n1n-1 条边组成最小生成树。首先建出这棵树 TT;令第 ii 条边为 eie_i,由于边权互不相同,所以对于一条不在 TT 上的边 ei=(u,v)(in)e_i=(u,v)(i\ge n),它的权值必然大于 TTuvu\leftrightarrow v 路径上所有边权的最大值。

    尝试进行一些操作:设 uvu\leftrightarrow v 路径上的边分别为 ep1,ep2,,epke_{p_1},e_{p_2},\ldots,e_{p_k},可以令 $l_i\gets\max\left\{l_i,\left(\max\limits_{j=1}^kl_{p_j}\right)+1\right\}$(操作一),并且对于路径上每一条边 epje_{p_j}rpjmin{rpj,ri1}r_{p_j}\gets\min\left\{r_{p_j},r_i-1\right\}(操作二)。

    根据最小生成树的性质可知,这样做仅仅筛掉了一些不合法的解,并不会把正确的解判漏;但是据此可以观察到一个非常有用的性质:进行完所有操作之后,对于所有不在 TT 上的边 ei=(u,v)e_i=(u,v)TTuvu\leftrightarrow v 路径包含的某条边 eje_j,都满足 li>ljri>rjl_i>l_j\land r_i>r_j!这意味着如果执行最开始的贪心流程,最终必然有 wi>wjw_i>w_j,满足条件。

    直接模拟上面的流程,操作一使用树剖 + ST 表维护链上最大值,操作二可以按照 rir_i 从小到大覆盖到链上(转换为区间覆盖后用并查集维护)。令 n,mn,m 同阶,时间复杂度为 O(nlogn)O(n\log n)

    放代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> pii;
    typedef tuple<int,int,int> tpi;
    typedef tuple<int,int,int,int> edge;
    namespace IAOI_lib{
      class dsu{
        private:
          vector<int> a;
        public:
          dsu(int n):a(n){
            iota(a.begin(),a.end(),0);
          }
          int leader(int x){
            return a[x]==x?x:a[x]=leader(a[x]);
          }
          void merge(int x,int y){
            x=leader(x),y=leader(y);
            if(x!=y)a[x]=y;
          }
      }; // 并查集模板
      template<typename T,T(*op)(T,T)> class sparse_table{
        private:
          vector<vector<T> > s;
        public:
          sparse_table(vector<T> a){
            int k=__lg(a.size());
            s.resize(k+1,vector<T>(a.size()));
            for(int i=0;i<a.size();i++)
              s[0][i]=a[i];
            for(int i=1;i<=k;i++)
              for(int j=0;j+(1<<i)<=a.size();j++)
                s[i][j]=op(s[i-1][j],s[i-1][j+(1<<i-1)]);
          }
          T query(int l,int r){
            int k=__lg(r-l+1);
            return op(s[k][l],s[k][r-(1<<k)+1]);
          }
      }; // ST 表模板
    }
    vector<int> cover(int n,vector<tpi> v){
      vector<int> f(n,-1);
      IAOI_lib::dsu d(n);
      for(auto [l,r,c]:v)
        for(int i=l;i<=r;i++){
          if(~f[i])i=d.leader(i);
          else if(f[i]=c;i<n-1&&(i<r||~f[i+1]))d.merge(i,i+1);
        }
      return f;
    } // 区间覆盖(P2391),使用并查集维护
    inline int mx(int x,int y){return x>y?x:y;}
    class hld{
      private:
        int n;
        vector<vector<tpi> > g;
        vector<int> f,d,e,h,t,l;
        vector<tpi> q;
        optional<IAOI_lib::sparse_table<int,mx> > s;
      public:
        hld(vector<vector<tpi> > g)
          :n(g.size()),g(g),f(n),d(n),e(n,1),h(n,-1),t(n),l(n){
          auto dfs=[&](auto &&self,int u)->void{
            int mx=0;
            for(auto [i,p,w]:g[u])
              if(i!=f[u]){
                f[i]=u,d[i]=d[u]+1;
                self(self,i),e[u]+=e[i];
                if(e[i]>mx)mx=e[i],h[u]=i;
              }
          };
          int o=0;
          vector<int> lv(n);
          auto decomp=[&](auto &&self,int u,bool b)->void{
            t[u]=b?t[f[u]]:u,l[u]=o++;
            if(~h[u])self(self,h[u],true);
            for(auto [i,p,w]:g[u])
              if(i!=f[u]){
                if(i!=h[u])self(self,i,false);
                lv[l[i]]=w;
              }
          };
          f[0]=-1,dfs(dfs,0),decomp(decomp,0,false);
          s.emplace(lv);
        }
        int query(int u,int v,int c){
          int w=-1;
          auto go=[&](int l,int r){
            if(l>r)return;
            w=max(w,s->query(l,r));
            q.emplace_back(l,r,c);
          };
          while(t[u]!=t[v]){
            if(d[t[u]]>d[t[v]])swap(u,v);
            go(l[t[v]],l[v]),v=f[t[v]];
          }
          if(d[u]>d[v])swap(u,v);
          return go(l[u]+1,l[v]),w;
        } // 一条非树边造成的影响
        vector<int> get_r(){
          auto r=cover(n,q);
          vector<int> rs(n-1);
          auto dfs=[&](auto &&self,int u)->void{
            for(auto [i,p,w]:g[u])
              if(i!=f[u])rs[p]=r[l[i]],self(self,i);
          };
          return dfs(dfs,0),rs;
        } // 求解所有操作二过后的 r
    }; // 树剖模板
    int main(){
      ios::sync_with_stdio(false);
      cin.tie(0); cout.tie(0);
      int t; cin>>t;
      while(t--){
        int n,m; cin>>n>>m;
        vector<edge> e(m);
        for(auto &[u,v,l,r]:e)
          cin>>u>>v>>l>>r,u--,v--,l--,r--;
        vector<vector<tpi> > t(n);
        vector<int> lv(n-1);
        for(int i=0;i<n-1;i++){
          auto [u,v,l,r]=e[i];
          lv[i]=l;
          t[u].emplace_back(v,i,l);
          t[v].emplace_back(u,i,l);
        }
        hld h(t);
        vector<int> p(m);
        iota(p.begin(),p.end(),0);
        sort(p.begin(),p.end(),[&](int x,int y){
          return get<3>(e[x])<get<3>(e[y]);
        }); // 按照 r 排序,方便操作二
        for(int i:p){
          if(i<n-1)continue;
          auto &[u,v,l,r]=e[i];
          l=max(l,h.query(u,v,r)+1);
        } // 枚举非树边进行操作
        auto rs=h.get_r();
        for(int i=0;i<n-1;i++){
          auto &[u,v,l,r]=e[i];
          if(~rs[i])r=min(r,rs[i]-1);
        }
        bool f=true;
        vector<tpi> c(m);
        for(int i=0;i<m&&f;i++){
          auto [u,v,l,r]=e[i];
          if(l>r||l>=m||r<0)f=false;
          c[i]=make_tuple(l,r,i);
        }
        sort(c.begin(),c.end());
        priority_queue<pii,vector<pii>,greater<> > q;
        vector<int> w(m);
        for(int i=0,p=0;i<m&&f;i++){
          while(p<m&&get<0>(c[p])==i){
            auto [l,r,i]=c[p++];
            q.emplace(r,i);
          }
          if(q.empty()||q.top().first<i)f=false;
          else w[q.top().second]=i,q.pop();
        } // 贪心求解排列 w 的过程
        if(f){
          cout<<"Yes\n";
          for(int i:w)cout<<i+1<<' ';
          cout<<'\n';
        }
        else cout<<"No\n";
      }
      return 0;
    }
    
    • 1

    信息

    ID
    7096
    时间
    2500ms
    内存
    2048MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者