[ARC208D] Symmetric Matrix 题解
前言
这或许是我目前离做出 AtCoder
过去已经凝固,我带着回忆向前。 谨以此题解纪念我 AtCoder 比赛生涯中的一次遗憾。
解法
提出一个与官方题解不同的思路。
前置知识:完全图边染色
判掉两个显然无解的情况:(1)No。以下讨论不考虑这些情况。
先不考虑
观察到最终构造出的方阵是一个拉丁方(每行 / 列上的所有元素都不相同的方阵;“每列上的元素也不相同”是根据对称的性质得出的)。立即联想到 [SNOI2024] 拉丁方,虽然 SNOI 这题的二分图边染色的思路不太好拓展到本题(因为本题“对称”的条件比较棘手,二分图边染色不能处理这种“两条边颜色相同”的关系),但是这启发了一种新思路:“对称”的限制即为
答案是肯定的,这样的转换完美保留了拉丁方原有的所有性质,并且能够解决对称的限制。进一步发现这就是一个完全图边染色的问题:给定一个完全图
这个问题的解法可以参考文章开头给出的知乎回答链接(当然下文也同样会提到)。解决问题需要对完全图点数的奇偶性分类,接下来我们就来尝试对应地进行讨论。
N 为偶数
此时
前面忽略了
考虑
N 为奇数
特判掉
此时
为方便下文叙述,这里与
考虑
尝试进一步考虑边染色的本质,能够得知除了结点
上述的思考启发我们对行和列的编号进行置换,这样就能解决如下的情况:存在恰好一个满足
现在剩下一种情况不能处理:存在
我们讨论完了所有的情况,直接对着模拟构造即可通过此题。时间复杂度
实现
代码中的数组是 0-indexed 的。
放代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t; cin>>t;
while(t--){
int n; cin>>n;
vector<int> a(n),ps(n);
// a 数组对应文中的 Y 数组
for(auto &i:a)cin>>i,i--;
iota(ps.begin(),ps.end(),0);
if(!is_permutation(a.begin(),a.end(),ps.begin())){
cout<<"No\n"; continue;
} // 如果不是排列无解
bool f=true;
int c=0;
for(int i=0;i<n;i++)
f&=a[a[i]]==i,c+=i==a[i];
if(!f){cout<<"No\n"; continue;} // 存在 Y[i]!=i,无解
vector w(n,vector<int>(n));
if(!(n&1)){
for(int i=0;i<n-1;i++){
w[0][i+1]=w[i+1][0]=i+1;
for(int l=(i+(n-1)-1)%(n-1),r=(i+1)%(n-1),c=0;c<n-2>>1;l=(l+n-2)%(n-1),r=(r+1)%(n-1),c++)
w[l+1][r+1]=w[r+1][l+1]=i+1;
} // 完全图边染色
for(int i=0;i<n;i++)
if(w[i][a[i]])swap(w[i][a[i]],w[i][i]); // 交换构造
} // 偶数的构造
else{
if(c>1){cout<<"No\n"; continue;} // 多于一个 Y[i]=i,无解
vector wp(n,vector<int>(n));
for(int i=0;i<n;i++)
for(int l=i,r=i,c=0;c<n+1>>1;l=(l+n-1)%n,r=(r+1)%n,c++)
wp[l][r]=wp[r][l]=i;
// 略有修改的完全图边染色
vector<int> v1,v2;
for(int i=0;i<n;i++)
if(i==a[i])v1.emplace_back(i);
for(int i=0;i<n;i++)
if(i<a[i]){
v1.emplace_back(i);
v1.emplace_back(a[i]);
} // 求匹配 M1
for(int l=0,r=0,c=0;c<n+1>>1;l=(l+n-1)%n,r=(r+1)%n,c++){
v2.emplace_back(l);
if(l!=r)v2.emplace_back(r);
} // 求匹配 M2
vector<int> p(n);
for(int i=0;i<n;i++)
p[v1[i]]=v2[i];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
w[i][j]=wp[p[i]][p[j]]; // 替换操作
} // 奇数的构造
cout<<"Yes\n";
for(auto i:w){
for(int j:i)cout<<j+1<<' ';
cout<<'\n';
}
}
return 0;
}