Luogu P10179 水影若深蓝 题解 [ 绿 ] [ 并查集 ] [ 构造 ]
水影若深蓝:挺好的一道并查集构造题。
观察
不难发现“距离为
这个染色问题就很并查集。
于是我们用并查集维护相同的种类。
显然,当图上只有一个连通块的时候,无解;否则我们一定可以找到一组解,因为一棵树一定可以进行黑白染色。
朴素想法
注意到每次连边只能连两个颜色不同的点的边,并且还要满足某些点之间距离为
每次用并查集合并完颜色后,我们重新遍历一遍那些限制,然后扫一遍所有点,找到一个与他们颜色不同的点,分别向他们连一条边。
然后剩下的点因为没有限制,所以瞎几把乱连就好,这个和 MST 一样的实现方式。
但这样是
优化
因为同一个连通块里的点可以找同一个中转点,所以我们可以构造一个菊花,先钦定一个颜色不同的点(不在同一个连通块内),然后把某连通块所有的点与它连边,就可以让他们之间的距离全部为
构造
进一步优化,可以发现我们只要找到两个不同集合里的点就好了,不用对于每个连通块一个一个找中转点。
我们选择两个处于不同连通块的祖先
最后注意
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
int f[300005],n,m,a[300005],b[300005];
void init()
{
for(int i=1;i<=n;i++)f[i]=i;
}
int findf(int x)
{
if(f[x]!=x)f[x]=findf(f[x]);
return f[x];
}
void combine(int x,int y)
{
int fx=findf(x),fy=findf(y);
f[fx]=fy;
}
void solve()
{
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
cin>>a[i]>>b[i];
combine(a[i],b[i]);
}
bool ilg=1;
for(int i=1;i<=n;i++)
{
if(i>1)ilg=(ilg&(findf(i)==findf(i-1)));
}
if(ilg==1)
{
cout<<"No"<<endl;
return;
}
cout<<"Yes"<<endl;
int p1=-1,p2=-1;
for(int i=1;i<=n;i++)
{
int fa=findf(i);
if(p1==-1)p1=fa;
else if(p2==-1&&fa!=p1)p2=fa;
}
for(int i=1;i<=n;i++)
{
int fa=findf(i);
if(i==p1)continue;
if(fa==p1)cout<<i<<" "<<p2<<endl;
else cout<<i<<" "<<p1<<endl;
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)solve();
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战