最近终于把这个坑填了 ⋯ \cdots ⋯
什么是拓扑排序
对一个有向无环图
G
G
G进行拓扑排序,是将
G
G
G中所有顶点排成一个线性序列,使得图中
任意一对顶点
u
u
u和
v
v
v,若边
(
u
,
v
)
∈
E
(
G
)
(u,v)∈E(G)
(u,v)∈E(G),则
u
u
u在线性序列中出现在
v
v
v之前。
拓扑排序的实现步骤
- 在有向图中选一个没有前驱的顶点并且输出
- 从图中删除所有和它有关的边)
- 重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者
代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
注意事项
- 如果 A A A在 B B B之后,那么连边时是从 B → A B\to A B→A连一条有向边(不要连反了啊)
- 判断有没有环:拓扑排序一遍之后,如果是 D A G DAG DAG所有点都恰好入队一次,如果有环,那么一定存在没有入队的点。
- 如何判断是不是拓扑:图论题 & & \&\& &&不是最短路 & & \&\& &&点与点之间有先后顺序
bool topsort(){ //拓扑排序
int tot;
tot=0; //记录出队列的元素个数,判断是否有环
for(int i=1;i<=n;i++)
if(!r[i])
{
q.push(i);
/*
根据题意
*/
}
while(!q.empty()) //tot是顶点个数
{
int x=q.front();
tot++; q.pop();
for(int i=head[x];i;i=e[i].nex)
{
int yy=e[i].y;
r[yy]--; //把所有从x到达的边的入度都减去1;
if(!r[yy]) //如果yy的入度为0,则说明从x到y的这条边是最后一条边
{
q.push(yy); //入度变为0的入队
/*
根据题意
*/
}
}
}
if(tot==n) return true;
else return false;
}
例题
最大食物链计数
显然拓扑,如果一个点最后删到没有前驱也没有后继,那么它就是一条链的末尾
注意不要漏
%
\%
%,不然只有
40
p
t
s
40pts
40pts
#include <bits/stdc++.h>
#define int long long
using namespace std;
const long long Mod=80112002;
const int N=5000050;
struct node{
int nxt,to;
}e[N];
int fir[N],in[N],out[N];
queue<int> q;
int tot;
int n,m,a,b;
int f[N],ans;
inline void add(int x,int y){
e[++tot].nxt=fir[x];
e[tot].to=y;
fir[x]=tot;
}
inline void topo(){
for(int i=1;i<=n;++i){
if(!in[i]) f[i]=1,q.push(i);
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=fir[x];i;i=e[i].nxt){
int v=e[i].to;
in[v]--;f[v]+=f[x];f[v]%=Mod;
if(!in[v]){
if(out[v]==0){
ans+=f[v];
ans=ans%Mod;
}
else q.push(v);
}
}
}
}
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<1)+(cnt<<3)+(c^48);c=getchar();}
return cnt*f;
}
signed main(){
freopen("1.in","r",stdin);
n=read(),m=read();
for(int i=1;i<=m;++i){
a=read(),b=read();
add(b,a);in[a]++;out[b]++;
}
topo();
cout<<ans%Mod<<endl;
return 0;
}