这个题说实话有点难度。
dfs跑两次,或者bfs跑两次,一次是解决不了的。
第一次,找出入度为1,或入度大于等于2的点,打上标记,然后将没有访问到的点和他的出边一并删除,并标记为0。
第二次,再次遍历,将到某一点的标记沿着路径传递(第一步不会传递标记,比如当结点4标记为2时,他的下一个节点,假设为6,标记还是1,所以需要传递)。
然后常规拓扑排序,当遇到环以后,所有与环相关的点都无法被访问到,就标记为-1即可
最后遍历标记就ok了
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int M=4e5+5;
vector<int> c[M];
int ind[M];
int vis[M];
int ans[M];
void dfs1(int u){
vis[u]=1; ans[u]=1;
for(int i=0;i<c[u].size();i++){
int p=c[u][i];
if(vis[p]){
ans[p]=2;
continue;
}
dfs1(p);
}
}
void dfs2(int u,int now){
vis[u]=1;
if(ans[u]==2) now=2;
ans[u]=now;
for(int i=0;i<c[u].size();i++){
int p=c[u][i];
if(vis[p]) continue;
dfs2(p,now);
}
}
void topo(){
if(ans[1]==2){
for(int i=1;i<=n;i++){
if(ans[i]) ans[i]=-1;
}
return;
}
queue<int> q;
q.push(1);
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=1;
for(int i=0;i<c[u].size();i++){
int p=c[u][i];
ind[p]--;
if(!ind[p]) q.push(p);
}
}
for(int i=1;i<=n;i++){
if(!vis[i]&&ans[i]) ans[i]=-1;
}
}
int main(){
int t; cin>>t;
while(t--){
cin>>n>>m;
for(int i=1;i<=n;i++){
c[i].clear();
ind[i]=0;
vis[i]=0;
ans[i]=0;
}
for(int i=1;i<=m;i++){
int u,v; cin>>u>>v;
c[u].push_back(v);
ind[v]++;
}
dfs1(1);
for(int i=1;i<=n;i++){
if(!vis[i]){
for(int j=0;j<c[i].size();j++){
int p=c[i][j];
ind[p]--;
}
}
}
for(int i=1;i<=n;i++) vis[i]=0;
dfs2(1,1);
for(int i=1;i<=n;i++){
vis[i]=0;
}
topo();
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
cout<<endl;
}
}