点这里 |
---|
题意: 有n个点和m条有向边,根节点T。输出所有与根节点有直接边,并且不能通过其他路径到达根节点的所有节点。
题解: 最容易能够想到DFS,一开始题意理解偏差了一直没写对,完全修改正确了之后交了TLE。一些细节不知道怎么处理就加了一个tarjan强连通分量的模板。后来再找大佬的题解,发现一些细节处理好了就不需要用到tarjan。
- 反向建图。合法的路径都是到达根节点的,但是要从根节点开始搜索,自然需要反向建边。
- 搜索起点。为了处理方便,建边时舍弃u->root的边,将这些直接连接根节点的边插入一个vector中,建完图之后以这里面的点作为起点开始DFS。
- 剪枝。 解决TLE的关键所在,定义一个set类型的vis[i]记录能搜索到i点的dfs起点。如果某个点的vis中记录这一个以上的dfs起点,说明含有多条路径到达根节点,排除答案,并且下次再搜索到的时候可以直接return。
#include<bits/stdc++.h>
#define bug printf("bug*****************\n")
using namespace std;
typedef long long ll;
inline int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int N = 1e5 + 10;
int cnt;
int head[N];
vector<int> V;
vector<int> ans;
set<int> vis[N];
struct edge{ int v, next;} e[N];
void addedge(int u, int v){ e[cnt].v = v, e[cnt].next = head[u], head[u] = cnt++;}
void dfs(int u, int fa){
if(vis[u].size() > 1 || vis[u].count(fa)) return;
vis[u].insert(fa);
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].v;
dfs(v, fa);
}
}
void run(){
for(int i = 0; i < N; i++) head[i] = -1;
int n = read(), m = read(), t = read();
for(int i = 0; i < m; i++){
int v = read(), u = read();
if(u == t) V.push_back(v);
else addedge(u, v);
}
for(int i = 0; i < V.size(); i++)
dfs(V[i], V[i]);
for(int i = 0; i < V.size(); i++)
if(vis[V[i]].size() == 1)
ans.push_back(V[i]);
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for(int i = 0; i < ans.size(); i++)
printf("%d\n", ans[i]);
}
int main(){
run();
return 0;
}