求出拓扑序,显然只有前面的点向后面的点连边。这样倒序处理每个点,对它的所有出点按照拓扑序正序处理,如果已经能到达就是多余的边。这个过程可以用bitset维护。
#include<cstdio>
#include<bitset>
#include<algorithm>
using namespace std;
const int maxn=30010,maxm=100010;
int fir[maxn],ne[maxm],to[maxm],que[maxn],ord[maxn],du[maxn],f[maxn],n,m;
bitset<maxn> succ[maxn];
int cmp(int x,int y)
{
return ord[x]<ord[y];
}
int main()
{
int u,v,hd=1,tl=0,ans=0,num;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
ne[i]=fir[u];
fir[u]=i;
to[i]=v;
du[v]++;
}
for (int i=1;i<=n;i++)
if (!du[i]) que[++tl]=i;
while (hd<=tl)
{
u=que[hd];
ord[u]=hd++;
for (int i=fir[u];i;i=ne[i])
{
du[to[i]]--;
if (!du[to[i]]) que[++tl]=to[i];
}
}
for (int i=n;i;i--)
{
num=0;
u=que[i];
succ[u][u]=1;
for (int j=fir[u];j;j=ne[j]) f[++num]=to[j];
sort(f+1,f+num+1,cmp);
for (int j=1;j<=num;j++)
if (succ[u][f[j]]) ans++;
else succ[u]|=succ[f[j]];
}
printf("%d\n",ans);
}