【bzoj4484】【JSOI2015】【最小表示】【拓扑排序+bitset】

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

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;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值