洛谷 P11337 「COI 2019」IZLET 题解

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

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

如果存在边连接的两个点颜色相同怎么办?考虑本质上可以把同样颜色的点构成的连通块“缩”成一个点(两个点 \(u,v\) 在同一个同色连通块里的充要条件为 \(c_{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;
}
posted @ 2024-12-19 20:30  FFTotoro  阅读(1)  评论(0编辑  收藏  举报