Description
【故事背景】
还记得去年JYY所研究的强连通分量的问题吗?去年的题目里,JYY研究了对于有向图的“加边”问题。对于图论有着强烈兴趣的JYY,今年又琢磨起了“删边”的问题。
【问题描述】
对于一个N个点(每个点从1到N编号),M条边的有向图,JYY发现,如果从图中删去一些边,那么原图的连通性会发生改变;而也有一些边,删去之后图的连通性并不会发生改变。
JYY想知道,如果想要使得原图任意两点的连通性保持不变,我们最多能删掉多少条边呢?
为了简化一下大家的工作量,这次JYY保证他给定的有向图一定是一个有向无环图(JYY:大家经过去年的问题,都知道对于给任意有向图的问题,最后都能转化为有向无环图上的问题,所以今年JYY就干脆简化一下大家的工作)。
Input
输入一行包含两个正整数N和M。
接下来M行,每行包含两个1到N之间的正整数x_i和y_i,表示图中存在一条从x_i到y_i的有向边。
输入数据保证,任意两点间只会有至多一条边存在。
N<=30,000,M<=100,000
Output
输出一行包含一个整数,表示JYY最多可以删掉的边数。
Sample Input
5 6
1 2
2 3
3 5
4 5
1 5
1 3
1 2
2 3
3 5
4 5
1 5
1 3
Sample Output
2
题解:
先求出这个图的拓扑序.
按拓扑序倒着做.中途可以求出一个点到出度为0的点的最大距离.
访问到一个点x的时候.
把它所有指向的点按之前求的距离从大到小排序.
然后枚举每个它指向点.如果这个点在之前已经可以从当前点访问到,那这条边显然多余.
用bitset维护一下每个点和其他点的联通情况即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<algorithm>
#define N 30010
#define M 100010
using namespace std;
int point[N],n,m,x,y,t,next[M],d[N],q[N],cnt,len[N],end,ans;
bitset<N>bit[N];
struct edge{int st,en;}e[M];
struct use{int en,v;}a[N];
inline int read(){
int x(0);char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y){
next[++cnt]=point[x];point[x]=cnt;
e[cnt].st=x;e[cnt].en=y;
}
inline bool cmp(use a,use b){
return a.v>b.v;
}
inline void solve(){
int h(0),t(0),num(0);
for (int i=1;i<=n;i++) if (!d[i]) q[++t]=i;
while (h<t){
int u=q[++h];
for (int i=point[u];i;i=next[i]){
d[e[i].en]--;if (!d[e[i].en]) q[++t]=e[i].en;
}
}
for (int i=t;i>=1;i--){
int u=q[i];bit[u][u]=1;num=0;len[u]=1;
for (int j=point[u];j;j=next[j]){
a[++num]=use{e[j].en,len[e[j].en]};
len[u]=max(len[u],len[e[j].en]+1);
}
sort(a+1,a+num+1,cmp);
for (int j=1;j<=num;j++){
int y=a[j].en;//cout<<u<<' '<<y<<endl;
if (bit[u][y]) ans++;
bit[u]|=bit[y];
}
}
}
int main(){
n=read();m=read();
for (int i=1;i<=m;i++){
x=read();y=read();
add(x,y);d[y]++;
}
solve();
cout<<ans<<endl;
}