拓扑序的定义:给定一张有向无环图,其中的n个点构成一个序列,且对于这个图中的每一条边(x,y)都满足:在序列中,点x一定在点y之前。这样的排列顺序就叫做拓扑序,这个排列过程叫拓扑排序。
拓扑序存在的条件:这里再强调一下:一定是有向且无环!不满足这个条件的图一定是没有拓扑序的!但是如果你看到了题目中给定了有向无环图这一条件或者你通过分析转化得到了一张有向无环图,那么你就可以往拓扑排序这一块去想了。
拓扑排序
例题1.AcWing 164.可达性统计
这个题目一上来就给了一个条件“有向无环图”。那么考虑一下拓扑排序。从拓扑序的特性来看,我们如果按照拓扑序对图进行遍历的话,那么每一个点可到达的点的数量都是它自身引出的所有点可到达的点数之和(且排除了重复的点)。那么思路就很清晰了,我们只要按照拓扑序的逆序来做就可以了,每一次求出底部的节点可达的点的数量,不断向上更新即可。
但是还有一个问题:如何去重呢? 又是很容易地想到利用位运算,把每个点到达各个点的情况用一个n位的二进制数来表示,0表示不可达,1表示可达。对于i的每一个可直接通过一条边抵达的点j,容易知道有f[i] = f[i] | f[j]。然后对于每一个i,输出f[i]表示的数中1的数量即可。但是n的范围太大了,这怎么办呢?
这里需要引进一个新的数据结构:bitset。可以用于存储多位的二进制数,常用于状态压缩。 这个数据结构可香了,可以支持我们上面去重所需要的一切操作。
代码如下:
#include<iostream>
#include<bitset>
#include<queue>
using namespace std;
const int N=3e4+5;
int head[N],to[N],ne[N],idx;
void add(int x,int y){
ne[++idx]=head[x];
to[idx]=y;
head[x]=idx;
}
int n,m;
int d[N],seq[N],cnt;
bitset<N> f[N];
void topsort(){
//拓扑排序的板子,直接放在这里了
queue<int> q;
for(int i=1;i<=n;i++){
if(!d[i]) q.push(i);
}
while(q.size()){
int t=q.front();
q.pop();
seq[++cnt]=t;
for(