acwing 164. 可达性统计(拓扑排序+位运算,bitset使用)

题目传送门

题目描述

给定一张 NN 个点 MM 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。

输入格式

第一行两个整数 N,MN,M,接下来 MM 行每行两个整数 x,yx,y,表示从 xx 到 yy 的一条有向边。

输出格式

输出共 NN 行,表示每个点能够到达的点的数量。

数据范围

1≤N,M≤300001≤N,M≤30000

输入样例:

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

  1. 首先进行一次topo排序,记录拓扑排序的顺序

  2. 然后倒序遍历拓扑排序的顺序,对于逆序中每个点

    • 首先该点可以到达它自己

    • 然后记录该点能够到达其他的所有点(用bitset 加 或运算表示)

      • 遍历该点能到的所有点(邻接表),用bitset 加 或运算表示该点能到的所有点
  3. 然后遍历一遍所有点,看每个点能够到达点的数量(bitset中1的个数)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue> 
#include<algorithm>
#include<bitset>
using namespace std;
const int N = 30010;
const int M = N * 2;

int h[N], e[M], ne[M], idx = 0;
int d[N]; // 入度 
int seq[N], cnt = 0; // 每个点到其他点的距离 

bitset<N> f[N]; // 前面的N表示每个bitset的长度
			    // 后面的N表示有N个bitset 

void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}
int n, m;

void topo()
{
	queue<int> q;
	for(int i = 1; i <= n; i++)
		if(!d[i]) 
		{
			seq[cnt++] = i;
			q.push(i);
		}
		
	
	while(q.size())
	{
		int t = q.front();
		q.pop();
		
		
		
		for(int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if(!d[j]) continue;
			
			d[j]--;
			if(!d[j])
			{
				seq[cnt++] = j;
				q.push(j);
			}
		}
	}
}


int main()
{	
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof(h));

	while(m --)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y); 
		d[y]++; // 入度++
	} 
	
	topo();
	
	// 倒序遍历topo排序 
	for(int i = n-1; i >= 0; i--) 
	{
		
		int j = seq[i]; // j: 1 -> n中某个数 
		
		f[j][j] = 1; // j点的第j位为1
		for(int k = h[j]; k != -1; k = ne[k])
		{
			f[j] |= f[e[k]]; // j点能到的所有点包括其指向的点能到的点的并集	
		} 
	}
	
	for(int i = 1; i <= n; i++) printf("%d\n", f[i].count());
	return 0; 
}

时间复杂度

参考文章

https://www.acwing.com/solution/content/32117/

posted @ 2022-03-16 22:33  VanHope  阅读(47)  评论(0编辑  收藏  举报