显然我们先缩点,之后转化为一个 DAG,设为 \(G\),设由其反边构成的图为 \(G'\)。题意就是求所有 “好的” 点,其中一个 “好的” 点需要满足这个点在 \(G\) 上能走到的点和在 \(G'\) 上能走到的点的并集为所有点。
思路 1:显然一个点不可能同时在 \(G\) 和 \(G'\) 上都能被同一个点 \(u\) 走到(除了这个点就是 \(u\)),于是我们可以在 \(G\) 和 \(G'\) 上分别求出 \(u\) 能走到的点的个数,再加起来判断是否等于 \(n+1\)。但你发现这个能走到的点的个数并不好快速求。(如果用 bitset+拓扑 那么与直接暴力无异)
我们可以将限制改的更严一点:我们先对 \(G\) 随便跑一个拓扑序出来,假设点 \(u\) 的拓扑序为 \(a_u\)。那么一个点 \(u\) 如果是 “好的” 点,当且仅当:在 \(G\) 上,拓扑序比 \(u\) 小的点都能走到 \(u\),拓扑序比 \(u\) 大的点 \(u\) 都能走到。
思路 2:注意到这个条件其实告诉了我们一个隐含的信息:某个 “好的” 点 \(u\) 一定满足 \(u\) 在拓扑序的位置唯一。但是在考场上并没有想出来按这个思路做应该怎么做。
我们还是回到原来的要求:一个点 \(u\) 如果是 “好的” 点,当且仅当:在 \(G\) 上,拓扑序比 \(u\) 小的点都能走到 \(u\),拓扑序比 \(u\) 大的点 \(u\) 都能走到。
对于一个点 \(u\),我们可以先考虑哪些点 \(u\) 走不到,那么 \(u\) 走不到的点肯定不是 “好的” 点。我们可以找出 \(u\) 的所有儿子中拓扑序最小的那个点 \(v\),那么拓扑序在 \((a_u,a_v)\) 之间的点 \(u\) 肯定走不到,那么我们对区间 \((a_u,a_v)\) 打上标记。
我们可以同样考虑哪些点走不到 \(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
*/
标签:ch,社保,int,拓扑,++,low,序比,XSY3325
From: https://www.cnblogs.com/ez-lcw/p/16840748.html