拓扑排序
演示过程(来自参考博客:https://blog.csdn.net/qq_41713256/article/details/80805338)
第一步:对所有入度为0的点入队列
第二步:bfs搜索队列,并把该入度为0的点顺序存储到新数组中,再把所有与该点相连的点的入度减一
如上图所示,即实现对任意u->v,u都在v的左边
可达性统计
时间限制: 1 Sec 内存限制: 128 MB
提交: 110 解决: 33
[提交] [状态] [命题人:admin]
题目描述
给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。N,M≤30000。
输入
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。
输出
共N行,表示每个点能够到达的点的数量。
样例输入
复制样例数据
10 10
3 8
2 3
2 5
5 9
5 9
2 3
3 9
4 8
2 10
4 9
样例输出
1
6
3
3
2
1
1
1
1
1
概念
1:入度:对一个点来说有指向该点的点,就说该点有入度,否则就没有
2:出度:与入度相反,对某一点来说有没有指向别的点
3:” | “,或运算,和bitset结合,一种新方法。。
关于如何通过“ | ”,实现统计数量,大体理解是:
对于n个点,经过拓扑排序后,实现了对任意u->v,u都在v的左边
再逆序统计每个点后面与该点相连的数量
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<queue>
#include<bitset>
typedef long long ll;
const int INF=1e9+7;
const int xmax=3e4+7;
using namespace std;
int head[xmax*2],cnt;
int inder[xmax]; ///每个点的入度
int tot;
int n,m,u,v;
int a[xmax];
bitset<xmax> c[xmax];
struct node{
int to;
int next;
}edge[xmax*2];
void add(int u,int v)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
inder[v]++;
}
void toposort() ///拓扑排序
{
queue<int> q;
for(int i=1;i<=n;i++){
if(inder[i]==0) q.push(i);
}
while(!q.empty()){
int t=q.front();q.pop(); ///取出队列中首元素
a[++tot]=t;
///对每一个入度为0的点进入a[]数组中,直到所有点都排完序,使得所有u->v的点,经过拓扑排序后都使u在v前面
for(int i=head[t];i;i=edge[i].next){ ///链式前向星
int tmp=edge[i].to;
inder[tmp]--; ///所有与t相连的点的入度减一
if(inder[tmp]==0) q.push(tmp); ///当tmp这个点的入度为0,就加入队列q中
}
}
}
void solve()
{
int t1,t2;
for(int i=tot;i>=1;i--){ ///从后往前,因为最后一个点没有出度
t1=a[i];
c[t1][t1]=1; ///(根据题意包含它本身,所以初始化为1)
for(int j=head[t1];j;j=edge[j].next){
t2=edge[j].to;
c[t1]|=c[t2]; ///‘|’是 或 运算,对两个数进行二进制运算,对每一位 有一为一,如1|3=1|(11)=(11)=3,()表示二进制
// cout<<c[t1][t1]<<endl;
}
}
}
int main()
{
cin>>n>>m;
while(m--){
cin>>u>>v;
add(u,v);
}
toposort();
solve();
for(int i=1;i<=n;i++)
cout<<c[i].count()<<endl;
return 0;
}