可达性统计(拓扑排序 + 状态压缩)

可达性统计 题目地址(牛客)

一道比较经典的拓扑排序题

题目描述

给定一张\(N\)个点\(M\)条边的有向无环图,分别统计从每个点出发能够到达的点的数量。\(N,M≤30000\)

题解

设从点 u 出发能够到达的点构成的集合是 f(u),从点 u 出发能够到达的点,是从 u 的各个后继节点 v 出发能够到达的点的并集,再加上点 u 自身。先按照拓扑排序算法求出拓扑序,然后按照拓扑序的倒叙进行计算------因为在拓扑序中,任意一条边 (u ,v),u 都排在 v 之前。倒序处理即可。

Code

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<queue>
#define N 30007
using namespace std;
inline int read() {
	int x=0,f=1; char ch=getchar();
	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
	return x * f;
}
int n,m,cnt,tot;
int head[N],du[N],tp[N];	//tp拓扑序数组 
struct Edge {
	int next,to;
}edge[N<<1];
inline void add(int u,int v) {
	edge[++cnt].next = head[u];
	edge[cnt].to = v;
	head[u] = cnt;
}
void topo() {
	queue<int> q;
	for(int i=1;i<=n;++i)
		if(!du[i]) q.push(i);
	while(!q.empty()) {
		int u = q.front(); q.pop();
		tp[++tot] = u;
		for(int i=head[u];i;i=edge[i].next) {
			int v = edge[i].to;
			du[v]--; if(!du[v]) q.push(v);
		}
	}
}
int main()
{
	n = read() ,m = read();
	for(int i=1,u,v;i<=m;++i) {
		u = read() ,v = read();
		add(u,v); du[v]++;
	}
	topo();
	bitset<N> f[N];
	for(int i=cnt;i>=1;--i) {
		int u = tp[i];
		f[u][u] = 1;
		for(int j=head[u];j;j=edge[j].next) {
			int v = edge[j].to;
			f[u] |= f[v];	//只要有就继承一下儿子 
		}
	}
	for(int i=1;i<=n;++i)
		printf("%d\n",f[i].count());
	return 0;
}
posted @ 2019-08-16 15:59  基地AI  阅读(223)  评论(0编辑  收藏  举报