显然我们先缩点,之后转化为一个 DAG,设为 G G G,设由其反边构成的图为 G ′ G' G′。题意就是求所有 “好的” 点,其中一个 “好的” 点需要满足这个点在 G G G 上能走到的点和在 G ′ G' G′ 上能走到的点的并集为所有点。
思路 1:显然一个点不可能同时在 G G G 和 G ′ G' G′ 上都能被同一个点 u u u 走到(除了这个点就是 u u u),于是我们可以在 G G G 和 G ′ G' G′ 上分别求出 u u u 能走到的点的个数,再加起来判断是否等于 n + 1 n+1 n+1。但你发现这个能走到的点的个数并不好快速求。(如果用 bitset+拓扑 那么与直接暴力无异)
我们可以将限制改的更严一点:我们先对 G G G 随便跑一个拓扑序出来,假设点 u u u 的拓扑序为 a u a_u au。那么一个点 u u u 如果是 “好的” 点,当且仅当:在 G G G 上,拓扑序比 u u u 小的点都能走到 u u u,拓扑序比 u u u 大的点 u u u 都能走到。
思路 2:注意到这个条件其实告诉了我们一个隐含的信息:某个 “好的” 点 u u u 一定满足 u u u 在拓扑序的位置唯一。但是在考场上并没有想出来按这个思路做应该怎么做。
我们还是回到原来的要求:一个点 u u u 如果是 “好的” 点,当且仅当:在 G G G 上,拓扑序比 u u u 小的点都能走到 u u u,拓扑序比 u u u 大的点 u u u 都能走到。
对于一个点 u u u,我们可以先考虑哪些点 u u u 走不到,那么 u u u 走不到的点肯定不是 “好的” 点。我们可以找出 u u u 的所有儿子中拓扑序最小的那个点 v v v,那么拓扑序在 ( a u , a v ) (a_u,a_v) (au,av) 之间的点 u u u 肯定走不到,那么我们对区间 ( a u , a v ) (a_u,a_v) (au,av) 打上标记。
我们可以同样考虑哪些点走不到 u u u。
最后可以证明没有被打上标记的点一定是 ”好的“ 点。
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,m;
int cnt=1,head[N],nxt[N<<1],to[N<<1];
int idx,dfn[N],low[N];
int top,sta[N];
int nscc,scc[N];
int num,du[N],a[N];
int c[N];
bool ins[N];
bool cor[N];
bool F;
vector<int>e[N];
void tarjan(int u)
{
dfn[u]=low[u]=++idx;
sta[++top]=u,ins[u]=1;
for(int v:e[u])
{
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
nscc++;
int v;
do
{
v=sta[top],top--;
ins[v]=0;
scc[v]=nscc;
}while(v!=u);
}
}
void adde(int u,int v)
{
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
queue<int>q;
void tuopu()
{
num=0;
for(int i=1;i<=nscc;i++)
if(!du[i]) q.push(i);
while(!q.empty())
{
int u=q.front();
q.pop();
a[u]=++num;
for(int i=head[u];i;i=nxt[i])
{
if((i&1)^F) continue;
int v=to[i];
du[v]--;
if(!du[v]) q.push(v);
}
}
}
void work()
{
for(int i=1;i<=nscc;i++) du[i]=0;
for(int i=2+F;i<=cnt;i+=2) du[to[i]]++;
tuopu();
if(num!=nscc)
{
puts("0");
exit(0);
}
for(int i=1;i<=nscc;i++) c[i]=0;
for(int u=1;u<=nscc;u++)
{
int minv=0;
for(int i=head[u];i;i=nxt[i])
{
if((i&1)^F) continue;
if(!minv||a[to[i]]<a[minv]) minv=to[i];
}
if(minv) c[a[u]+1]++,c[a[minv]]--;
else c[a[u]+1]++,c[nscc+1]--;
int maxv=0;
for(int i=head[u];i;i=nxt[i])
{
if(!((i&1)^F)) continue;
if(!maxv||a[to[i]]>a[maxv]) maxv=to[i];
}
if(maxv) c[a[maxv]+1]++,c[a[u]]--;
else c[1]++,c[a[u]]--;
}
for(int i=1;i<=nscc;i++) c[i]+=c[i-1];
for(int i=1;i<=nscc;i++) if(c[a[i]]) cor[i]=0;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[u].push_back(v);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int u=1;u<=n;u++)
{
for(int v:e[u])
{
if(scc[u]!=scc[v])
{
adde(scc[u],scc[v]);
adde(scc[v],scc[u]);
}
}
}
for(int i=1;i<=nscc;i++) cor[i]=1;
F=0;work();
vector<int>ans;
ans.clear();
for(int i=1;i<=n;i++)
if(cor[scc[i]]) ans.push_back(i);
printf("%d\n",(int)ans.size());
for(int u:ans) printf("%d ",u);
return 0;
}
/*
6 7
1 2
1 3
2 4
3 4
4 5
5 6
6 5
*/