P11337 「COI 2019」IZLET 题解
如果每条边连接的两个点颜色都不相同,那么可以使用如下策略确定每个点的颜色:
令
如果存在边连接的两个点颜色相同怎么办?考虑本质上可以把同样颜色的点构成的连通块“缩”成一个点(两个点
放代码:
#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;
}