有N(N<=10000)头牛,每头牛都想成为most poluler的牛,给出M(M<=50000)个关系,如(1,2)代表1欢迎2,关系可以传递,但是不可以相互,即1欢迎2不代表2欢迎1,但是如果2也欢迎3那么1也欢迎3.
给出N,M和M个欢迎关系,求被所有牛都欢迎的牛的数量。
Input
第一行N,M
接下来M行A,B,表示A欢迎B
Output
一个数,表示答案
Examples
Sample Input
3 3
1 2
2 1
2 3
Sample Output
1
Hint
题意:
求和所有点都联通的点的数量
题解:
一道经典的强连通分量基础题, 一共有三种方法, 这里我一一实现
第一种方法: Korosaju的拓扑序
我们知道通过Korosaju算法可以求出缩点后SCC的拓扑序, 这样在拓扑序最后的点就一定是与所有点都联通, 这样我们算一下拓扑序最后一个点SCC的实际点的数目
记得再检测一下是否真正与所有点联通, 防止图不联通
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1<<30;
const LL maxn = 10010;
int N, M;
vector<int> G[maxn];
vector<int> rG[maxn];
vector<int> vs;
bool used[maxn];
int topo[maxn];
void Dfs(int u){
used[u] = true;
for(int v = 0; v < G[u].size(); ++v)
if(!used[G[u][v]])
Dfs(G[u][v]);
vs.push_back(u);
}
void rDfs(int u, int k){
used[u] = true;
topo[u] = k;
for(int v = 0; v < rG[u].size(); ++v)
if(!used[rG[u][v]])
rDfs(rG[u][v], k);
}
int scc(){
ms(used, 0);
for(int i = 1; i <= N; ++i)
if(!used[i])
Dfs(i);
ms(used, 0);
int k = 1;
for(int i = vs.size()-1; i >= 0; --i)
if(!used[vs[i]])
rDfs(vs[i], k++);
return k-1;
}
int main()
{
int a, b;
cin >> N >> M;
for(int i = 1; i <= M; ++i){
cin >> a >> b;
G[a].push_back(b);
rG[b].push_back(a); //构建反向图
}
int n = scc();
int u = 1, ans = 0;
for(int v = 1; v <= N; ++v)
if(topo[v]==n)
u = v, ++ans;
ms(used, 0);
rDfs(u, 1); //最后再搜一下, 防止图不连通
for(int i = 1; i <= N; ++i)
if(!used[i]){
ans = 0;
break;
}
cout << ans << endl;
return 0;
}
第二种方法: Korosaju的出度表
因为要知道使用Korosaju算法更重要的是求出了各个点所属的SCC, 这样我们针对缩点后的SCC图建立出入度表, 可以想到在DAG上与所有点都联通的点一定出度为0, 且只能有一个, 多个的话就说明图不连通
最后统计一下原图上在该SCC中点的数目
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1<<30;
const LL maxn = 10010;
int N, M;
vector<int> G[maxn];
vector<int> rG[maxn];
vector<int> vs;
bool used[maxn];
int topo[maxn];
int in[maxn], out[maxn];
void Dfs(int u){
used[u] = true;
for(int v = 0; v < G[u].size(); ++v)
if(!used[G[u][v]])
Dfs(G[u][v]);
vs.push_back(u);
}
void rDfs(int u, int k){
used[u] = true;
topo[u] = k;
for(int v = 0; v < rG[u].size(); ++v)
if(!used[rG[u][v]])
rDfs(rG[u][v], k);
}
int scc(){
ms(used, 0);
for(int i = 1; i <= N; ++i)
if(!used[i])
Dfs(i);
ms(used, 0);
int k = 1;
for(int i = vs.size()-1; i >= 0; --i)
if(!used[vs[i]])
rDfs(vs[i], k++);
return k-1;
}
int main()
{
int a, b;
cin >> N >> M;
for(int i = 1; i <= M; ++i){
cin >> a >> b;
G[a].push_back(b);
rG[b].push_back(a); //构建反向图
}
int n = scc();
int cnt = 0, tag, ans = 0;
for(int u = 1; u <= N; ++u)
for(int i = 0; i < G[u].size(); ++i){
int v = G[u][i];
if(topo[u] != topo[v])
++out[topo[u]], ++in[topo[v]];
}
for(int i = 1; i <= n; ++i)
if(out[i]==0)
++cnt, tag = i;
if(cnt!=1)
cout << "0" << endl;
else{
for(int i = 1; i <= N; ++i)
if(topo[i]==tag)
++ans;
cout << ans << endl;
}
return 0;
}
第二种方法: Tarjan的出度表
Tarjan算法同样可以在O(N+M)的复杂度下求出所点后的SCC, 其他原理同上一个一样.
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1<<30;
const LL maxn = 50010;
int N, M, ecnt = 1, head[maxn];
struct node{
int v, next;
}es[maxn];
void addEdge(int u, int v){
es[ecnt].v = v, es[ecnt].next = head[u];
head[u] = ecnt++;
}
int indx = 0, scc = 0;
bool ins[maxn];
int dfn[maxn], low[maxn], belong[maxn];
stack<int> s;
void tarjan(int u){
int v;
dfn[u] = low[u] = ++indx;
s.push(u);
ins[u] = true;
for(int i = head[u]; i != -1; i = es[i].next){
v = es[i].v;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]){
++scc;
do{
v = s.top();
s.pop();
ins[v] = false;
belong[v] = scc;
}while(u!=v);
}
}
int out[maxn], in[maxn];
int main()
{
int a, b;
ms(head, -1);
cin >> N >> M;
for(int i = 1; i <= M; ++i){
cin >> a >> b;
addEdge(a, b);
}
for(int i = 1; i <= N; ++i){
if(!dfn[i])
tarjan(i);
}
for(int u = 1; u <= N; ++u){
for(int i = head[u]; i != -1; i = es[i].next){
int v = es[i].v;
if(belong[u] != belong[v])
++out[belong[u]], ++in[belong[u]];
}
}
int cnt = 0, ans = 0, tag;
for(int i = 1; i <= scc; ++i)
if(out[i]==0)
++cnt, tag = i;
if(cnt!=1) cout << "0" << endl;
else{
for(int i = 1; i <= N; ++i)
if(belong[i] == tag)
++ans;
cout << ans << endl;;
}
return 0;
}