P10062 [SNOI2024] 拉丁方 题解

FFTotoro

2024-02-24 08:59:15

题解

看到这种矩阵构造又有奇怪约束的题目,考虑建图解决。

但是 $C<n$ 的情况可能会出现无解。考虑把原矩阵补成一个 $C=n$ 的矩阵(就是把右上角那一块填上)。仍然使用上面的做法,这次由**值**向**行**连边,然后每条边的颜色就是其所在的列。注意如果使用的颜色数 $p$ 与 $n-C$ 不相等,说明无解。 使用找 $\mathrm{mex}$ 然后调整冲突的方法进行二分图边染色,封装成一个类似乎比较好写。代码实现中使用了 C++17 中的 `std::variant`,使用方法见[这里](https://en.cppreference.com/w/cpp/utility/variant)。 放代码: ```cpp #include<bits/stdc++.h> #define int short using namespace std; class EdgeColoring{ private: vector<int> d; vector<vector<int> > a; // a[i][j] 表示 i 结点出发的颜色为 j 的边到达哪个点 public: EdgeColoring(int n,int m){ d.resize(n+m); a.resize(n+m,vector<int>(n+m,-1)); } // 初始化 int add_edge(int u,int v){ d[u]++,d[v]++; // 记录度数,判断无解用 int c1=0,c2=0; while(~a[u][c1])c1++; while(~a[v][c2])c2++; // 找 mex a[u][c1]=v,a[v][c2]=u; if(c1!=c2) // 有冲突 for(int c3=c2,i=v;~i;i=a[i][c3],c3^=c1^c2) swap(a[i][c1],a[i][c2]); // 调整 return c1; } pair<int,vector<vector<int> > > color(){ return make_pair(*max_element(d.begin(),d.end()),a); } // 返回值前者为颜色数,后者为染色方案 }; // 二分图边染色 variant<vector<vector<int> >,bool> f1(int n,vector<vector<int> > &a){ vector s(a.size(),vector<int>(n)); for(int i=0;i<a.size();i++) for(int j=0;j<a[0].size();j++) s[i][j]=a[i][j]; EdgeColoring g(n,a.size()); for(int i=0;i<a.size();i++){ vector<bool> b(n); for(int j=0;j<a[0].size();j++) b[a[i][j]-1]=true; for(int j=0;j<n;j++) if(!b[j])g.add_edge(j,i+n); } // 建图 auto [p,m]=g.color(); // 染色 if(p!=n-a[0].size())return false; // 无解 for(int i=0;i<n;i++) for(int j=0;j<n-a[0].size();j++) if(~m[i][j])s[m[i][j]-n][j+a[0].size()]=i+1; // 构造 return s; } vector<vector<int> > f2(int n,vector<vector<int> > &a){ vector s(n,vector<int>(n)); for(int i=0;i<a.size();i++) s[i]=a[i]; EdgeColoring g(n,n); for(int i=0;i<n;i++){ vector<bool> b(n); for(int j=0;j<a.size();j++) b[a[j][i]-1]=true; for(int j=0;j<n;j++) if(!b[j])g.add_edge(j,i+n); } auto m=g.color().second; for(int i=0;i<n;i++) for(int j=0;j<n-a.size();j++) if(~m[i][j])s[j+a.size()][m[i][j]-n]=i+1; return s; } main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n,r,c; cin>>n>>r>>c; vector a(r,vector<int>(c)); for(auto &i:a)for(auto &j:i)cin>>j; if(auto s=f1(n,a);get_if<bool>(&s))cout<<"No\n"; else{ auto s2=f2(n,get<vector<vector<int> > >(s)); cout<<"Yes\n"; for(auto i:s2){ for(int j:i)cout<<j<<' '; cout<<'\n'; } } } return 0; } ```