正题
题目链接:https://www.luogu.com.cn/problem/P6134
题目大意
给出一张\(n\)个点\(m\)条边的\(DAG\)。求联通情况不变的情况下最多删除几条边。
\(1\leq n\leq 3\times 10^4,0\leq M\leq 10^5\)
解题思路
拓扑排序后,如果确定了后面若干个的最优解,那么不会影响到前面的决策,我们只需要对于每个点考虑删除最多的出边即可。
从后往前枚举,对于一个点连接的集合\(E\),按照拓扑序从小到大排后,每次加入一个点和它所有连接的点,如果该点已经联通,那么这条边就可以删除了。
用\(bitset\)可以快速实现这个过程。
时间复杂度\(O(\frac{mn}{w})\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<queue>
#include<vector>
using namespace std;
const int N=3e4+10;
int n,m,cnt,tot,ans;
int in[N],ls[N],top[N],tfn[N];
queue<int> q;vector<int> v;
bitset<N> b[N];
struct node{
int to,next;
}a[N<<2];
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void topsort(){
for(int i=1;i<=n;i++)
if(!in[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
top[++cnt]=x;tfn[x]=cnt;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
in[y]--;
if(!in[y])q.push(y);
}
}
return;
}
bool cmp(int x,int y)
{return tfn[x]<tfn[y];}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
addl(x,y);in[y]++;
}
topsort();
for(int p=n;p>=1;p--){
int x=top[p];v.clear();b[x][x]=1;
for(int i=ls[x];i;i=a[i].next)
v.push_back(a[i].to);
sort(v.begin(),v.end(),cmp);
for(int i=0;i<v.size();i++){
int y=v[i];
if(b[x][y]) ans++;
else b[x]|=b[y];
}
}
printf("%d\n",ans);
}