Problem
给点t,求有多少点使得断开其与t直接相连的边后没有从其到t的路径,求出这些点。
Solution
首先建反向图,此时变成了“单源”问题,记可能为答案的点组成集合S,以当前点与出发点组成的二元组为状态进行bfs,如果当前点与出发点相同就不增加对它的访问次数,也不再对它往外bfs。如果能够到达一个点多次说明这个点不满足要求。
上述做法如果中间出现环会有无限次进队,因此考虑剪枝,一个一般的点如果能被两个不同的出发点到达那么此时从第三个及以上的出发点到达该点所组成的状态已经不会对最终答案产生影响,因此一个当前点最多进队两次且来自两个不同出发点(来自同出发点的该点多次进队更无意义)。
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=1e5+5;
int n,m,t,cnt,last[maxn],ans[maxn],ac[maxn],num,vis[maxn];
bool inq[maxn];
struct edge{
int v,next;
}e[maxn];
struct node{
int u,scr;
};
void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].next=last[u];
last[u]=cnt;
}
void bfs()
{
queue<node>q;
for(int i=last[t];i;i=e[i].next)
q.push({e[i].v,e[i].v});
while(!q.empty())
{
node nd=q.front();
int u=nd.u,scr=nd.scr;
q.pop();
if(ans[u]&&u==scr) continue;
if(ans[u]>2) continue;
ans[u]++;
for(int i=last[u];i;i=e[i].next)
{
int v=e[i].v;
if(v!=t)
{
if(!vis[v]||vis[v]&&vis[v]!=scr)
{
q.push({v,scr});
vis[v]=vis[v]?vis[v]:scr;
}
}
}
}
}
int main()
{
cin>>n>>m>>t;
for(int i=1,u,v;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(v,u);
}
bfs();
for(int i=last[t];i;i=e[i].next)
{
if(ans[e[i].v]==1)
ac[++num]=e[i].v;
}
sort(ac+1,ac+1+num);
cout<<num<<endl;
for(int i=1;i<=num;i++)
cout<<ac[i]<<endl;
return 0;
}