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;
}

Submission

posted @ 2026-09-25 08:14  fl0ppy-  阅读(13)  评论(0)    收藏  举报