P6134 [JSOI2015]最小表示 题解

P6134 [JSOI2015]最小表示

首先有向无环图,考虑拓扑排序,接下来按照 (BFS) 来理解整个拓扑排序。

相当于把所有的点分成了若干层,(每一层中的点互不联通)。

那么保留最少的边一定是将每一层形成一个类似链的关系,也就是应该优先保留 (u) 能够到达的更浅的层的边。

于是对于当前的点 (u) ,每一条边按照出点 (v) 的时间戳排序。

因为是最优策略,所以按照上面的次序访问边 (u->v) 如果说 (u,v) 已经联通,这个边可以删去,连通性和最优性都可以保证,用一个 bitset 维护连通性就好了。

Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e4+2,M=1e5+3;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch))	f|=ch=='-',ch=getchar();
	while(isdigit(ch))	x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	x=f?-x:x;
}
template <typename T>
inline void print(T x){
	if(x<0)	putchar('-'),x=-x;
	if(x>9)	print(x/10);
	putchar(x%10^48);
}
bitset<N> bis[N];
int n,m;
int head[N],Next[M],to[M],dfn[N],tot,in[N],tim,rev[N];

int ju[N];

inline void Addedge(int u,int v){to[++tot]=v,Next[tot]=head[u],head[u]=tot;return;}

inline bool Cmp(int x,int y){return dfn[x]<dfn[y];}

inline void Topo(){
	queue<int> q;
	for(register int i=1;i<=n;++i)	if(!in[i])		q.push(i);		
	while(!q.empty()){
		int x=q.front();q.pop();
		dfn[x]=++tim;rev[tim]=x;
		for(register int i=head[x];i;i=Next[i]){
			int y=to[i];
			if(!(--in[y])) q.push(y);
		}
	}
	int ans=0;
	for(register int i=n;i;--i){
		int x=rev[i];
		bis[x][x]=1;
		int siz=0;
		for(register int j=head[x];j;j=Next[j])
			ju[++siz]=to[j];
		sort(ju+1,ju+siz+1,Cmp);
		for(register int j=1;j<=siz;++j){
			if(bis[x][ju[j]])	ans++;
			else bis[x]|=bis[ju[j]];
		}
	}
	print(ans);
	return ;
}

int main(){
	read(n),read(m);
	for(register int i=1;i<=m;++i){
		int u,v;read(u),read(v);
		Addedge(u,v);in[v]++;
	}
	Topo();
	return 0;
}	
原文地址:https://www.cnblogs.com/NuoCarter/p/14988041.html