有一个结论:对于边<u,v>,若这是u到v的唯一路径,则这条边显然不可被删去,否则必然可以被删去。
因为若u到v还有其它路径,则必然是从u到某个点x再到v,由于最终答案中连通性不变,也就是最终答案中仍然可以走到x后再走到v,于是可以删去这条边。
于是大致算法就出来了:按拓扑序从后往前做,每次将这个点的所有出边到达的点按拓扑序从小到大处理,若某个出点已经可以被到达了则删边。
bitset维护连通性,$O(nm/32)$
1 #include<cstdio> 2 #include<bitset> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 6 using namespace std; 7 8 const int N=100010; 9 int n,m,cnt,x,y,p[N],id[N],d[N],q[N],v[N],t,ans,h[N],to[N],nxt[N]; 10 bitset<30010> a[30010],b; 11 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 12 13 void Top(){ 14 int st=0,ed=0; 15 rep(i,1,n) if (!d[i]) q[++ed]=i,id[i]=ed; 16 while (st<ed){ 17 int x=q[++st]; 18 For(i,x) if (!--d[k=to[i]]) q[++ed]=to[i],id[to[i]]=ed; 19 } 20 } 21 22 bool cmp(const int &a,const int &b){ return id[a]<id[b]; } 23 24 int main(){ 25 scanf("%d%d",&n,&m); 26 rep(i,1,m) scanf("%d%d",&x,&y),add(x,y),d[y]++; 27 Top(); 28 for (int i=n; i; i--){ 29 int x=q[i],tot=0; a[x][x]=1; 30 For(j,x) v[++tot]=k=to[j]; 31 sort(v+1,v+tot+1,cmp); 32 rep(j,1,tot) if (a[x][v[j]]) ans++; else a[x]|=a[v[j]]; 33 } 34 printf("%d ",ans); 35 return 0; 36 }