正题
题目链接:https://www.luogu.com.cn/problem/P6134
题目大意
给出一张nnn个点mmm条边的DAGDAGDAG。求联通情况不变的情况下最多删除几条边。
1≤n≤3×104,0≤M≤1051\leq n\leq 3\times 10^4,0\leq M\leq 10^51≤n≤3×104,0≤M≤105
解题思路
拓扑排序后,如果确定了后面若干个的最优解,那么不会影响到前面的决策,我们只需要对于每个点考虑删除最多的出边即可。
从后往前枚举,对于一个点连接的集合EEE,按照拓扑序从小到大排后,每次加入一个点和它所有连接的点,如果该点已经联通,那么这条边就可以删除了。
用bitsetbitsetbitset可以快速实现这个过程。
时间复杂度O(mnw)O(\frac{mn}{w})O(wmn)
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);
}