可达性统计

题目描述
给定一张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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值