拓扑排序
题目
现有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;
}