题目:
阿天住的城市有 n 个政府部门,这些部门之间用了 m 条有向路径
连接了起来。有趣的是,每过一天这些路径都会变换方向,也就是说,
偶数的日子和奇数的日子的图是不同的。
阿天在社保局工作,可惜他过于丢人忘记了社保局的位置。他只
记得由于社保局很重要,它在一个可以到达所有其他部门的地点。请
你帮他找到所有满足条件的地点。
solution:
首先肯定要缩点,因为强连通分量不管怎样都会走到
然后考场上还想出了拓扑排序,但是处理方式不太对
我想的是分层然后如果这层只有一个点就可以,但之后举出了反例
正解应该是利用拓扑序和拓扑的性质
如果一个点可以到达的点和可以被到达的点加起来超过了
n
n
n那这个点就是答案点
考虑如何计算一个点
u
u
u可以到达哪些点,因为拓扑序有分层的特点
在去点
t
o
[
u
]
to[u]
to[u]中找到
i
d
[
v
]
id[v]
id[v]最小的,那么从
i
d
[
u
]
+
1
id[u]+1
id[u]+1到
i
d
[
v
]
−
1
id[v]-1
id[v]−1这一段都是不能被到达的,被到达和这个方法一样
就可以在拓扑序上差分然后前缀和,如果 s u m [ i ] = 0 sum[i]=0 sum[i]=0就是答案点
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 1000005
using namespace std;
int n,m,cnt,head[N],eh[N],ec,sum[N],num,id[N],pos[N],pre[N];
int dfn[N],low[N],stk[N],tot,top,tim,bel[N],d[N];
bool vis[N],can[N];
vector<int> scc[N],ans;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct EDGE{
int to,nxt;
}edge[N],e[N];
inline void add(int x,int y){
edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
inline void add2(int x,int y){
e[++ec].to=y; e[ec].nxt=eh[x]; eh[x]=ec; ++d[y];
}
void tarjan(int u){
dfn[u]=low[u]=++tim; vis[u]=1; stk[++top]=u;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
tot++; int tmp;
do{
tmp=stk[top--]; vis[tmp]=0;
bel[tmp]=tot; scc[tot].push_back(tmp);
}while(u!=tmp);
} return;
}
inline void rebuild(){
for(int u=1;u<=n;u++)
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(bel[v]!=bel[u]) add2(bel[u],bel[v]);
}
}
inline void solve(){
top=0;
for(int i=1;i<=tot;i++){
can[i]=true;
if(!d[i]) stk[++top]=i,id[i]=++num,pos[num]=i;
}
while(top){
int u=stk[top--];
for(int i=eh[u];i;i=e[i].nxt){
int v=e[i].to;
--d[v];
if(!d[v]) stk[++top]=v,id[v]=++num,pos[num]=v;
}
}
for(int u=1;u<=tot;u++)
for(int i=eh[u];i;i=e[i].nxt){
int v=e[i].to;
pre[v]=max(pre[v],id[u]);//找到连到v的id最大的点
}
for(int i=1;i<=num;i++){
int u=pos[i];
sum[pre[u]+1]++,sum[i]--;
}
for(int i=1;i<=num;i++){
pre[i]=tot+1; sum[i]+=sum[i-1];
if(sum[i]) can[pos[i]]=false;
}
memset(sum,0,sizeof sum);
for(int u=1;u<=tot;u++)
for(int i=eh[u];i;i=e[i].nxt){
int v=e[i].to;
pre[u]=min(pre[u],id[v]);//找到u连接的id最小的点
}
for(int i=1;i<=num;i++){
int u=pos[i];
sum[pre[u]-1]++,sum[i]--;
}
for(int i=num;i;i--){
sum[i]+=sum[i+1];
if(sum[i]) can[pos[i]]=false;
}
}
int main(){
freopen("wtmsb.in","r",stdin);
freopen("wtmsb.out","w",stdout);
n=rd(); m=rd();
for(int i=1;i<=m;i++){
int x=rd(),y=rd();
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
rebuild(); solve();
for(int i=1;i<=tot;i++)
if(can[i])
for(int j=0;j<scc[i].size();j++)
ans.push_back(scc[i][j]);
sort(ans.begin(),ans.end());
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++)
printf("%d ",ans[i]);
return 0;
}