P10179 水影若深蓝 题解
题意
对于一颗
Sol
对于每一条关系,用类似存无向图的方式记录,即
然后我们可以假设一个虚拟结点为
这样做的好处就是保证了
接着我们考虑所有
像这样类似拓扑排序的进行下去,用队列维护,每次取出队首,把所有与队首有关系的结点都让其与
但是这样的操作还存在两个缺陷,有的关系还没有在图上体现,
如何解决?我们可以再构造多个菊花图,对于两个不在同一个图上的点,他们之间没有关系,互不影响:
注意加黑的点,我们可以让
对于其他的虚拟结点,只需要让
没有任何关系的点也可以与
这样的正确性显然,两个图之间的点不存在任何关系,合并图只是让虚拟结点变成实际的结点,选用一个毫不相干的点作为其核心结点,当然不受影响。
对于无解情况,如果所有点都与
考虑一下代码如何实现。
初始化所有
取任意结点作为
遍历所有结点,如果该结点还没有与
特别的,如果所有结点都与
最后输出,如果
本题的核心思路在于想到菊花图,并且进行合并。
单次时间复杂度
Code
#include<bits/stdc++.h>
#define ll long long
#define N 400005
#define endl "\n"
#define fi first
#define se second
using namespace std;
const ll mod=1e9+7;
const ll inf=1e18;
const double eps=1e-6;
ll n,m,res[N],tr[3];
bool vis[N];
vector<ll>v[N];
queue<ll>q;
void sol(){
cin>>n>>m;
tr[1]=tr[2]=0;
for(int i=1;i<=n;i++){
v[i].clear();
vis[i]=0;
res[i]=0;
}
while(!q.empty())q.pop();
for(int i=1;i<=m;i++){
ll x,y;
cin>>x>>y;
v[x].push_back(y);
v[y].push_back(x);
}
bool fl=1;
for(int i=1;i<=n;i++){
if(!tr[1]){
tr[1]=i;
break;
}
}
q.push(tr[1]);
res[tr[1]]=2;
while(!q.empty()){
auto t=q.front();
q.pop();
vis[t]=1;
for(auto y:v[t]){
if(!vis[y]){
q.push(y);
res[y]=2;
}
}
}
for(int i=1;i<=n;i++){
if(!vis[i]){
tr[2]=i;
break;
}
}
if(!tr[2]){
cout<<"No\n";
return ;
}
q.push(tr[2]);
res[tr[2]]=inf;
while(!q.empty()){
auto t=q.front();
q.pop();
vis[t]=1;
for(auto y:v[t]){
if(!vis[y]){
q.push(y);
res[y]=1;
}
}
}
cout<<"Yes\n";
for(int i=1;i<=n;i++){
if(res[i]<=2){
if(res[i]==0)res[i]=1;
cout<<i<<" "<<tr[res[i]]<<endl;
}
}
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ll ttt;
cin>>ttt;
while(ttt--)sol();
return 0;
}