bzoj 4484
4484: [Jsoi2015]最小表示
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 501 Solved: 265
[Submit][Status][Discuss]
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维护一下每个点和其他点的联通情况即可.
总结: 贪心+动态规划+bitset维护连通性
神奇的是我写记忆话搜索总是re。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=30000+10; 4 int const M=100000+10; 5 struct edge{ 6 int to,nt; 7 }e[M]; 8 int n,m,h[N],cnt,f[N],ind[N],ans,t[N],q[N]; 9 bitset<N> s[N]; 10 int cmp(int x,int y){ 11 return f[x]>f[y]; 12 } 13 void add(int a,int b){ 14 e[++cnt].to=b; 15 e[cnt].nt=h[a]; 16 h[a]=cnt; 17 } 18 void solve(){ 19 int cl=0; 20 for(int i=1;i<=n;i++) 21 if(!ind[i]) q[cl++]=i; 22 for(int i=0;i<cl;i++){ 23 int x=q[i]; 24 for(int j=h[x];j;j=e[j].nt){ 25 int v=e[j].to; 26 if(!--ind[v]) q[cl++]=v; 27 } 28 } 29 for(int i=cl-1;i>=0;i--){ 30 int x=q[i],num=0; 31 f[x]=1; 32 s[x][x]=1; 33 for(int j=h[x];j;j=e[j].nt){ 34 int v=e[j].to; 35 t[++num]=v; 36 f[x]=max(f[x],f[v]+1); 37 } 38 sort(t+1,t+num+1,cmp); 39 for(int j=1;j<=num;j++) 40 if(s[x][t[j]]==0) 41 s[x]|=s[t[j]]; 42 else ans++; 43 } 44 } 45 int main(){ 46 scanf("%d%d",&n,&m); 47 for(int i=1;i<=m;i++){ 48 int x,y; 49 scanf("%d%d",&x,&y); 50 add(x,y); 51 ind[y]++; 52 } 53 solve(); 54 printf("%d\n",ans); 55 return 0; 56 } 57