CF999E Reachability from the Capital解题报告
CF999E Reachability from the Capital
校内想了半天并查集,很显然并查集只能搞无向的,这个连加边都是有向的,而且并查集会把链给合并,显然不对。
然后注意到,我们可以先从源点跑bfs,对所有本身已经可以从源点到的都打上标记,即vis[x] = true
然后怎么办呢?
注意到一个联通快内只要有一个点能到源点,那么其他所有点都可以到,我们只需要对所有的满足vis[x] == false的缩一下点,把所有的强连通分量看作一个点。
如果缩点后,一个点入度为0,那么表示没有其他点能到它,它满足我们要使从源点到任意一点的条件,直接ans++即可
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N = 2e5 + 10;
inline int read(){
int x = 0; bool f = 1; char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = 0;
for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return f ? x : -x;
}
int n, stk[N], ind[N], dfn[N], low[N];
bool instk[N], vis[N];
vector<int> e[N];
int m, s;
int tot, cnt, top, dcc[N];
inline void tarjan(int x){//缩点
dfn[x] = low[x] = ++tot;
instk[x] = true;
stk[++top] = x;
for(const auto y : e[x]){
if(!dfn[y]){
tarjan(y);
low[x] = min(low[y], low[x]);
}
else if(instk[y]){
low[x] = min(dfn[y], low[x]);
}
}
if(low[x] == dfn[x]){
int tmp;
cnt++;
do{
tmp = stk[top--];
instk[tmp] = false;
dcc[tmp] = cnt;
} while(tmp != x);
}
}
queue<int> q1;
inline void bfs(int x){//bfs
q1.push(x);
while(q1.size()){
int tmp = q1.front();
q1.pop();
if(vis[tmp]){
continue;
}
vis[tmp] = 1;
for(const auto o : e[tmp]){
if(vis[o]){
continue;
}
q1.push(o);
}
}
}
main(){
n = read(), m =read(), s = read();
for(int i = 1; i <= m; ++i){
int aa = read(), bb = read();
e[aa].push_back(bb);
}//加边
bfs(s);
for(int i = 1; i <= n; ++i){
if(!dfn[i] && !vis[i]){
tarjan(i);
}
}
for(int i = 1; i <= n; ++i){
for(const auto o : e[i]){
if(dcc[o] != dcc[i]){
ind[dcc[o]]++;//统计入度
}
}
}
int t1 = 0;
for(int i = 1; i <= cnt; ++i){//注意循环到cnt,不要写成n
if(!ind[i]){
t1++;
}
}
cout << t1 << endl;
}
浙公网安备 33010602011771号