题意:
给定n个点m条边的无权无向图,
给定起点s,要求计算在给定图的补图中,s到其他点的最短距离。
数据范围:n,m<=2e5
解法:
显然就是要在补图上进行bfs.
如果直接存补图的边,几乎就是存完全图了,边的数量太多没办法存.
在补图上bfs的技巧:
开n个set,分别命名为g[i],g[i]中存所有点i可到达的点.
再开一个set,命名为s,在s中存所有往前未被标记的点.
由于是bfs,对于当前点x,我们只需要往未被标记的点走就行了,
因此遍历集合s中的点v,如果v不在g[x]中,那么说明补图中存在x到v的边,
那么标记v,并且将v从集合s中删除.
如果忽略set操作的复杂度,这样的做法耗时和普通bfs是一样的,都是O(m),
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=2e5+5;
unordered_set<int>g[maxm];
unordered_set<int>s;//存未被访问过的节点
int d[maxm];
int n,m;
void bfs(int st){
for(int i=1;i<=n;i++){
d[i]=-1;
}
d[st]=0;
s.erase(st);
queue<int>q;
q.push(st);
while(q.size()){
int x=q.front();q.pop();
for(auto it=s.begin();it!=s.end();){
int v=*it;
it++;
if(g[x].find(v)==g[x].end()){//有边
d[v]=d[x]+1;
q.push(v);
s.erase(v);
}
}
}
}
void solve(){
cin>>n>>m;
s.clear();
for(int i=1;i<=n;i++){
g[i].clear();
s.insert(i);
}
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
g[a].insert(b);
g[b].insert(a);
}
int st;cin>>st;
bfs(st);
for(int i=1;i<=n;i++){
if(i==st)continue;
cout<<d[i];
if(i==n)cout<<endl;
else cout<<' ';
}
}
signed main(){
ios::sync_with_stdio(0);
int T;cin>>T;
while(T--){
solve();
}
return 0;
}