题目链接:HDU - 5413
显然,直接暴力维护拓扑序是不行的。
我们考虑从小到大遍历拓扑序,然后对于当前遍历的点,访问他的前驱节点,并且从大到小访问前驱点。
如果存在某个拓扑序大的点可以到这个点,那么当前的这条边就是冗余边。
因为直接维护可达性复杂度太高,故用Bitset优化。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e4+10;
int n,m,deg[N],res;
vector<int> g[N],v[N],ts; bitset<N> bit[N];
void Top(){
queue<int> q; for(int i=1;i<=n;i++) if(!deg[i]) q.push(i); ts.clear();
while(q.size()){
int u=q.front(); q.pop(); ts.push_back(u);
for(auto to:g[u]){
if(--deg[to]==0) q.push(to); v[to].push_back(u);
}
}
}
inline void solve(){
cin>>n>>m; res=0;
for(int i=1;i<=n;i++) g[i].clear(),v[i].clear();
for(int i=1,a,b;i<=m;i++) scanf("%d %d",&a,&b),g[a].push_back(b),deg[b]++;
Top();
for(int i=1;i<=n;i++) bit[i].reset(),bit[i].set(i);
for(int i=0;i<n;i++){
for(int j=v[ts[i]].size()-1;j>=0;j--){
int to=v[ts[i]][j];
if(bit[ts[i]][to]) res++;
bit[ts[i]]|=bit[to];
}
}
printf("%d\n",res);
}
signed main(){
int T; cin>>T;
while(T--) solve();
return 0;
}