1 条题解

  • 0
    @ 2025-8-24 23:06:33

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar FFTotoro
    龙猫

    搬运于2025-08-24 23:06:33,当前版本为作者最后更新于2024-11-26 19:33:35,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    如果每条边连接的两个点颜色都不相同,那么可以使用如下策略确定每个点的颜色:

    ci,jc_{i,j}iijj 的路径上的颜色数。对于每个点 uu,以其为根进行一次 dfs,往下找直到找到一个和它颜色相同的或者一个叶子就回溯,如果遇到颜色相同的就将它们在并查集上合并。考虑如何在上面的过程中判断一个点 vv 是否和它颜色相同:假设 uuvv 的路径上除了 uu 外离 uu 最近的点为 ss(即 uu 的某个儿子),只需要判断是否有 cu,v=cs,vc_{u,v}=c_{s,v}

    如果存在边连接的两个点颜色相同怎么办?考虑本质上可以把同样颜色的点构成的连通块“缩”成一个点(两个点 u,vu,v 在同一个同色连通块里的充要条件为 cu,v=1c_{u,v}=1,可以用并查集维护)。显然最终构造出的树中这个同色连通块的形态是不重要的,所以可以用上面的每个连通块的“代表元”建树(可以证明在图上随便跑出一棵生成树都是对的),跑上一段所述的流程,之后把连通块中的其他点全部连到这个点上即可。

    放代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> pii;
    namespace IAOI_lib{
      template<typename T> class dsu{
        private:
          vector<T> a;
          vector<int> s;
        public:
          dsu(int n=2e5){
            a.resize(n),s.resize(n,1);
            iota(a.begin(),a.end(),0);
          }
          T leader(T x){
            return a[x]==x?x:a[x]=leader(a[x]);
          }
          inline int size(T x){
            return s[leader(x)];
          }
          inline void merge(T x,T y){
            x=leader(x),y=leader(y);
            if(x==y)return;
            if(s[x]>s[y])swap(x,y);
            s[y]+=s[x],a[x]=y;
          }
          inline bool same(T x,T y){
            return leader(x)==leader(y);
          }
      }; // 并查集模板
    }
    int main(){
      ios::sync_with_stdio(false);
      cin.tie(0); cout.tie(0);
      int t,n; cin>>t>>n;
      vector a(n,vector<int>(n));
      for(auto &i:a)for(auto &j:i)cin>>j;
      IAOI_lib::dsu<int> d(n),d1(n),d2(n);
      for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
          if(a[i][j]==1)d.merge(i,j); // 在一个同色连通块里
      vector<vector<int> > g(n);
      vector<pii> e;
      for(int i=0;i<n;i++)
        if(i==d.leader(i))
          for(int j=i+1;j<n;j++)
            if(j==d.leader(j)&&a[i][j]==2&&!d1.same(i,j)){
              g[i].emplace_back(j);
              g[j].emplace_back(i);
              e.emplace_back(i,j),d1.merge(i,j);
            } // 用代表元建树
      for(int i=0;i<n;i++)
        if(i!=d.leader(i))
          e.emplace_back(i,d.leader(i));
          // 把同色连通块内其他结点连向代表元
      function<void(int,int,int,int)> dfs=[&](int u,int f,int r,int s){
        if(~s&&a[r][u]==a[s][u])
          d2.merge(r,u); // 颜色相同
        else for(int i:g[u])
          if(i!=f)dfs(i,u,r,s<0?i:s);
      }; // dfs 染色过程
      for(int i=0;i<n;i++)
        if(i==d.leader(i))dfs(i,i,i,-1);
      for(int i=0;i<n;i++)
        cout<<d2.leader(d.leader(i))+1<<' ';
      cout<<endl;
      for(auto [u,v]:e)
        cout<<u+1<<' '<<v+1<<'\n';
      return 0;
    }
    
    • 1

    信息

    ID
    11014
    时间
    2000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者