题意:
现在有一张有向图,从1开始遍历,问你点i的值是:
0(到达不了)
1(只有一条路从1到i)
2(有大于等于两条且从1到i的路径数量有限)
-1(从1到i的路径数量无限)
题解:
首先可以dfs一遍整张图,每个点最多走两次,这样就能将0,1,2三种情况求出来。
对于-1的情况,我们知道如果x是-1当且仅当它在1能够到达的环上或者1可以经过一个环到达x。那么考虑用tarjan求出强联通分量,然后缩点。
称一个scc是有效的如果这个scc的大小>=2,或者这个点有自环。
接下来dfs查看每个scc是否是有效的且1能够到达,或者1能够经过一个有效的scc到达它。
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
struct node{
int to,next;
}e[N];
int cnt,head[N];
void add(int x,int y){
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt++;
}
// scc
int low[N],dfn[N],scc[N],tim,num;
stack<int>st;
bool in[N];
// init
void init(int siz) {
memset(head,-1,sizeof(int)*(siz+1));
cnt=tim=num=0;
memset(dfn,0,sizeof(int)*(siz+1));
memset(in,0,sizeof(bool)*(siz+1));
while(!st.empty())
st.pop();
}
// deal
void tarjan(int x) {
low[x]=dfn[x]=++tim;
st.push(x);
in[x]=1;
for(int i=head[x];~i;i=e[i].next) {
int ne=e[i].to;
if(!dfn[ne])
tarjan(ne),low[x]=min(low[x],low[ne]);
else if(in[ne])
low[x]=min(low[x],dfn[ne]);
}
if(low[x]==dfn[x]) {
++num;
while(1) {
int v=st.top();
st.pop();
scc[v]=num;
in[v]=0;
if(x==v)
break;
}
}
}
vector<int>vec[N];
int ans[N],sum[N],can[N];
void dfs1(int x,int fa){
can[x]++;
for(int i=head[x];~i;i=e[i].next){
int ne=e[i].to;
if(ne==fa)continue;
if(can[ne]==2)continue;
dfs1(ne,x);
}
}
void dfs2(int x,int fa,int f){
if(f||sum[x]>=2)ans[x]=-1;
else ans[x]=1;
for(int i:vec[x]){
if(i==fa||ans[i]==-1)continue;
if(ans[i]==1&&ans[x]!=-1)continue;
dfs2(i,x,f||sum[x]>=2);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init(n);
int x,y;
while(m--){
scanf("%d%d",&x,&y);
add(x,y);
}
tarjan(1);
for(int i=1;i<=n;i++){
sum[scc[i]]++;
for(int j=head[i];~j;j=e[j].next){
int ne=e[j].to;
if(ne==i)sum[scc[i]]=2;
else vec[scc[i]].push_back(scc[ne]);
}
}
dfs1(1,0);
dfs2(scc[1],0,0);
for(int i=1;i<=n;i++){
if(ans[scc[i]]==-1)printf("-1%c",i==n?'\n':' ');
else printf("%d%c",can[i],i==n?'\n':' ');
}
for(int i=1;i<=n;i++)vec[i].clear(),sum[i]=ans[i]=can[i]=scc[i]=0;
}
return 0;
}