題目鏈接: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;
}