题目描述
给定一张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
这道题利用的是拓扑排序+bitset
这一步骤是用来建立拓扑排序,
拓扑排序主要思想是先将没有入度的点排在前面,然后再将这个点以及这个点的弧去掉,再从剩下的点找到没有入度的点,依次,直到所有的点都找完
void fun()//构成拓扑排序
{
int p = 1;
queue<int>a;
//先将入度为0的点全都压入队例中
for(int i = 1;i <= N;i++){
if(vis[i] == 0) a.push(i);
}
while(!a.empty()){
int k = a.front();
a.pop();
f[p++] = k;//将所有的点都排好序
//找到了需要放入队例中的点,再将以它为出度的点另一个点为入度的点,都依次减一
for(int j = 0;j < G[k].size();j++){
if(vis[G[k][j]] != 0){
vis[G[k][j]]--;//入度减1
if(vis[G[k][j]] == 0) a.push(G[k][j]); //如果入度为0,再次压入队例中
}
}
}
}
这段程序结束后,f中的值为
1 2 4 6 7 3 5 10 8 9
这个前面的点在图中肯定比后面的点先访问,(当然也会有并例的存在)
此时就要用到bitset
bitset<30005>a[30005];
这样的定义表示每一个a[?]的数据类型占30005位,每个点都可以访问自己,所以a[x][x]=1,表示肯定有一个,然后再访问与这个点相连的点中的1的个数,进行或运算
所以要从后往前
for(int i = N;i >= 1;i--){
int x = f[i];
a[x][x] = 1;//使在x的时候最少可以访问一个
for(int j = 0;j < G[x].size();j++){
a[x] = a[x] | a[G[x][j]]; //或运算,有两个1就为1,有一个1也是为1,所以不存在重复
}
}
a[i].count(),这个则表示a[i]里面1的个数,也就是与i这个点相连的点有个数
#include <stdio.h>
#include <vector>
#include <queue>
#include <bitset>
using namespace std;
vector<int>G[30005];
int ans;
int N,M;
bitset<30005>a[30005];
int f[300005];
int vis[300005];
void fun()//构成拓扑排序
{
int p = 1;
queue<int>a;
for(int i = 1;i <= N;i++){
if(vis[i] == 0) a.push(i);
}
while(!a.empty()){
int k = a.front();
a.pop();
f[p++] = k;
for(int j = 0;j < G[k].size();j++){
if(vis[G[k][j]] != 0){
vis[G[k][j]]--;//入度减1
if(vis[G[k][j]] == 0) a.push(G[k][j]);
}
}
}
}
int main()
{
scanf("%d %d",&N,&M);
for(int i = 0;i < M;i++){
int x,y;
scanf("%d %d",&x,&y);
int j;
for(j = 0;j < G[x].size();j++){
if(G[x][j] == y) break;
}
if(j == G[x].size()){
G[x].push_back(y);
vis[y]++;//统计入度
}
}
fun();
for(int i = N;i >= 1;i--){
int x = f[i];
a[x][x] = 1;
for(int j = 0;j < G[x].size();j++){
a[x] = a[x] | a[G[x][j]];
}
}
for(int i = 1;i <= N;i++){
printf("%lld\n",a[i].count());
}
return 0;
}