CF1470D Strange Housing【二分图】归纳构造
CF1470D Strange Housing
给定一个无向图,你要找到一个独立集,对于一条边,如果 \(u,v\) 都不在独立集里,则删去这条边。
求满足删完后原图仍联通的独立集。\(n\le 10^5\)。
先把原图不联通的情况判掉。
对于类似独立集/匹配染色的构造题,我们可以先从二分图入手,从特殊情况启发得到正解。
如果原图是二分图,那么我们随便选一侧的点染色,显然正确。我们假定对所有左部点染色。
现在考虑原图不是二分图的情况,在二分图且已经染完色的基础上,左部和右部的点之间都有边,但如果左部有边是不合法的,我们就调整二分图,使得左部的点之间都无边,而对于右部内部的边,删掉显然是不影响连通性的。
那我们维护构造左部的过程即可,先随便放一个点进去,将其相连的点都从左部待选点移出,再判断这些点相连的点是否仍在左部待选点里,如果是就放左部,否则放右部。可以发现,这样构造一定能结束。
#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
int read(){
char c=getchar();int h=0,tag=1;
while(!isdigit(c)) tag=(c=='-'?-1:1),c=getchar();
while(isdigit(c)) h=(h<<1)+(h<<3)+(c^48),c=getchar();
return h*tag;
}
void fil(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
}
const int N=3e5+500;
vector<int>s[N];
int lft[N],vis[N];
void dfsR(int x);
void dfsL(int x) {
vis[x]=1;
for(int y:s[x]) {
lft[y]=1;
}
for(int y:s[x]) {
dfsR(y);
}
}
vector<int>ans;
void dfsR(int x) {
vis[x]=1;
for(int y:s[x]) {
if(lft[y]==0) ans.push_back(y),lft[y]=1,dfsL(y);
}
}
void work() {
int n=read(),m=read();
for(int i=1;i<=n;i++) {
s[i].clear();vis[i]=lft[i]=0;
}
ans.clear();
for(int i=1;i<=m;i++) {
int u=read(),v=read();s[u].push_back(v);s[v].push_back(u);
}
lft[1]=1;
ans.push_back(1);
dfsL(1);
for(int i=1;i<=n;i++) {
if(vis[i]==0) return puts("NO"),void();
}
puts("YES");
cout<<ans.size()<<endl;
for(int x:ans) {
cout<<x<<" ";
}
cout<<"\n";
}
int main(){
// fil();
int T=read();
while(T--) work();
return 0;
}
实际上,如果不从二分图的角度,而是站在归纳法上看,或许更为清晰:
对于 \(n=1\),染色即可。
对于 \(n>1\),前 \(n-1\) 个点是一个合法的方案(连通图),我们希望加进 \(n\),如果 \(n\) 相连的点全部没染色,则 \(n\) 必须染色,否则存在一个相连的点染色,那么 \(n\) 就必须不能染色。显然这样也一定会结束并构造成功。