思路:用tarjan缩点以后,在带权并查集,需判断一个并查集里,是否含有强连通分量,如果有贡献就是点集大小,如果没有减1.
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 200005
#define maxx 100005
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int head[maxn],to[maxx],_next[maxx];
int edge;
void addEdge(int x,int y)
{
to[++edge]=y,_next[edge]=head[x],head[x]=edge;
}
inline int _min(int a,int b)
{
return a>b?b:a;
}
int DFN[maxn],low[maxn],ind;
bool inS[maxn];
int st[maxn],cnt;
int be[maxn],tot;
int w[maxn];
bool sign[maxn];
void tarjan(int u)
{
DFN[u]=low[u]=++ind;
st[++cnt]=u;
inS[u]=true;
for(int i=head[u];i;i=_next[i])
{
int v=to[i];
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])
{
++tot;int now;
do
{
++w[tot];
now=st[cnt--];
be[now]=tot;
inS[now]=false;
}while(now!=u);
if(w[tot]>1)sign[tot]=true;
}
}
int pre[maxn];
int _find(int x)
{
return pre[x]==x?x:pre[x]=_find(pre[x]);
}
int main()
{
cin>>n>>m;
int x,y;
for(int i=0;i<m;++i)
{
scanf("%d%d",&x,&y);
addEdge(x,y);
}
for(int i=1;i<=n;i++)
if(!DFN[i])tarjan(i);
for(int i=1;i<=tot;i++)pre[i]=i;
for(int u=1;u<=n;u++)
for(int i=head[u];i;i=_next[i])
{
int v=to[i];
if(be[u]!=be[v])
{
int uu=_find(be[u]);
int vv=_find(be[v]);
if(uu!=vv)
{
pre[vv]=uu;
w[uu]+=w[vv];
sign[uu]|=sign[vv];
}
}
}
int ans=0;
for(int i=1;i<=tot;++i)
if(pre[i]==i)
{
if(sign[i])ans+=w[i];
else ans+=w[i]-1;
}
cout<<ans<<endl;
return 0;
}