P6134[JSOI2015]最小表示【bitset,拓扑排序】

正题

题目链接: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);
}
原文地址:https://www.cnblogs.com/QuantAsk/p/14402284.html