拓扑排序

题目

现有n扇门,门之间有m种关系,门与门之间可以互相传送。将所有不能被传送到的门视为起点,将不能传送到其他门的门视为终点,利用这些传送关系,求出有多少种路线。
结果可能很大,请对998244353取模。
https://ac.nowcoder.com/acm/contest/82881/B

Input

5 7
2 3
1 2
4 5
1 3
2 5
3 4
3 5

Output

5

说明

共有
5 -> 4 -> 3 -> 2 -> 1
5 -> 4 -> 3 -> 1
5 -> 3 -> 1
5 -> 3 -> 2 -> 1
5 -> 2 -> 1
五条路线

题解

思路分析

经典拓扑排序题目

拓扑排序步骤
1.计算每个点的入度。
2.入度为0就加入队列。
3.当队列不为空则循环:
(1)取出队首元素并输出。
(2)遍历队首元素的连边,对应节点的入度 −1。
(3)当对应的节点入度为0就加入队列。

这题不同于原始模板题,需要处理出度从而确定枚举对象,同时记录的是路线的数量而不是某一条路线。
因此,我们可以令第i点有一个权值dis[i],只有当该点被走过的时候才能去更新下一个点。
最后枚举出度为0的点统计答案即可。

Code

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr);
using namespace std;
typedef long long LL;
const int N=5e3+5;
const int Mod=998244353;
vector<int> e[N];
signed main(){
	IOS;
	int n,m;cin>>n>>m;
    vector<int> in(n+1),out(n+1);
    for(int i=1;i<=m;i++){
        int x,y;cin>>x>>y;
        e[x].push_back(y);
        in[y]++;out[x]++;
    }
    vector<int> dis(n+1);
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(!in[i]){dis[i]=1;q.push(i);}
    }
    while(!q.empty()){
        auto t=q.front();
        q.pop();
        for(auto i:e[t]){
            dis[i]+=dis[t];
            dis[i]%=Mod;
            if(!--in[i]) q.push(i);
        }
    }
    LL ans=0;
    for(int i=1;i<=n;i++){
        if(!out[i]){//枚举终点统计答案
            ans+=dis[i];
            ans%=Mod;
        }
    }
    cout<<ans<<endl;
	return 0;
}

posted on 2024-06-03 18:41  TaopiTTT  阅读(2)  评论(0编辑  收藏  举报