Description
给定一个有向图,按顺序输出是 (1) 到 (n) 路径上的必经点且本身不在一个环内的所有点。
Solution
先缩点,建一个新图。对于包含在一个大于等于二的强连通分量内的点一定不会是答案,包含自环的点一定不是答案。从 (1) 和 (n) 分别 bfs 一遍,对能到达的点打上标记。那么必须同时包含两个标记才会成为答案。把不含两个标记的点删掉。又得到一个新图。会发现新图一个特点,就是先从 (1) 出发可以走不同路径,然后汇聚到一个点,再在出发又汇聚到一个点。实际上汇聚到的点就是答案。这些点可以通过拓扑排序求出,当入队时队列是空的,那么这就是一个可以汇聚到的点。
也可以发现这些点具有割点的性质,可以直接搞成无向图然后求割点。有点卡常。
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
const int N=5e5+7;
queue<int> Q;
vector<int> g[N],f[N],e[N];
int timer=0,scc=0,Tp=0,top=0;
int scc_sz[N],c[N],sta[N],ans[N],Id[N],dfn[N],low[N];
bool vis1[N],vis2[N],sef[N];
void tarjan(int u){
dfn[u]=low[u]=++timer;
sta[++top]=u;
for(int v:f[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(!c[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
int y; scc++;
do{scc_sz[c[y=sta[top--]]=scc]++;}while(y!=u);
if(scc_sz[scc]==1) Id[scc]=y;
else Id[scc]=0;
}
}
void Tarjan(int u){
int flag=0;
low[u]=dfn[u]=++timer;
for(int v:e[u]){
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
if(scc_sz[u]==1&&low[v]>=dfn[u]&&!sef[Id[u]]){
flag++; if(flag>1||u!=c[1]) ans[++Tp]=Id[u];
}
}
low[u]=min(low[u],dfn[v]);
}
}
int main(){
freopen("i.in","r",stdin);
freopen("i.out","w",stdout);
int T=read();
while(T--){
int n=read(),m=read();
for(int i=1;i<=scc;i++)
e[i].clear(),scc_sz[i]=0;
timer=scc=0;
for(int i=1;i<=n;i++){
vis1[i]=vis2[i]=sef[i]=0;
dfn[i]=low[i]=c[i]=0;
g[i].clear(),f[i].clear();
}
for(int i=1,u,v;i<=m;i++){
u=read(),v=read();
if(u==v) sef[u]=1;
else{
f[u].push_back(v);
g[v].push_back(u);
}
}
tarjan(1);
if(!dfn[n]){printf("0
");continue;}
Q.push(1),vis1[1]=1;
while(!Q.empty()){
int u=Q.front(); Q.pop();
for(int v:f[u]){
if(vis1[v]) continue;
else Q.push(v),vis1[v]=1;
}
}
Q.push(n),vis2[n]=1;
while(!Q.empty()){
int u=Q.front(); Q.pop();
for(int v:g[u]){
if(vis2[v]) continue;
else Q.push(v),vis2[v]=1;
}
}
for(int u=1;u<=n;u++){
if(!vis1[u]||!vis2[u]) continue;
for(int v:f[u]){
if(!vis1[v]||!vis2[v]||c[u]==c[v]) continue;
e[c[u]].push_back(c[v]);
e[c[v]].push_back(c[u]);
}
}
for(int i=1;i<=scc;i++) dfn[i]=low[i]=0;
timer=0,Tp=0;
if(scc_sz[c[n]]==1&&!sef[n]) ans[++Tp]=n;
Tarjan(c[1]);
if(scc_sz[c[1]]==1&&!sef[1]) ans[++Tp]=1;
printf("%d
",Tp);
for(int i=Tp;i;i--) printf("%d ",ans[i]); putchar('
');
}
}