元素-集合二分图
元素 - 集合二分图 + BFS
atcoder.jp/contests/ab…
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
void so(){
}int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m;cin>>n>>m;
vector<vector<int>>g(n+m);
for(int i=0;i<n;i++){
int a;cin>>a;
for(int j=0;j<a;j++){
int s;cin>>s;--s;
//集合i-元素s(偏移n)
g[i].push_back(s+n);
g[s+n].push_back(i);
}
}int s=n,t=m-1+n;
queue<int>que;
vector<int>dist(n+m,-1);
que.push(s);
dist[s]=0;
while(!que.empty()){
int v=que.front();
que.pop();
for(auto v2:g[v]){
if(dist[v2]==-1){que.push(v2);dist[v2]=dist[v]+1;}
}
}// 答案计算(最重要!)
// dist[t] : 元素1 → 元素M 的最短路径长度
// 路径格式:元素1 → 集合 → 元素 → 集合 → ... → 元素M
// 每 2 步 = 使用 1 个集合
// 集合数 = dist[t] / 2
// 操作数 = 集合数 - 1
// --------------------
cout<<dist[t]/2-1;
}
强连通分量
atcoder.jp/contests/ty…
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1<<18;
int n,m;int a[N],b[N];
bool us[N];
vector<int>g[N];
vector<int>h[N];
vector<int>I;
ll cnts;
//记录后序遍历序列
void dfs(int pos){
us[pos]=true;
for(int i:g[pos]){
if(!us[i])dfs(i);
}I.push_back(pos);
}
void dfs2(int pos){//反图
us[pos]=true;
cnts++;
for(int i:h[pos]){if(!us[i])dfs2(i);}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i]>>b[i];
g[a[i]].push_back(b[i]);
h[b[i]].push_back(a[i]);
}for(int i=1;i<=n;i++){
if(!us[i])dfs(i);
}ll ans=0;
reverse(I.begin(),I.end());
fill(us,us+n+1,false);
for(int i:I){
if(us[i])continue;
cnts=0;
dfs2(i);
ans+=cnts*(cnts-1ll)/2ll;
}
cout<<ans;
}
思路
原图 DFS: 能到达别人的点会后结束 反图 DFS: 从后结束的点开始搜 只能搜到互相可达的点 刚好就是一个强连通分量!