欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ACWing164可达性统计(拓扑排序+bitset)

程序员文章站 2022-07-12 16:39:25
...

题目传送门:https://www.acwing.com/problem/content/166/
题目大意:给定一个有向无环图,问你每个点能到达的点的数量。(N,M<= 30000)。
分析:
一、初看这可是一个妥妥的图的遍历的问题,枚举每个点,然后从每个点bfs或dfs一遍,看能访问到几个点计数即可,如下代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 30000+6;
struct node{
	int v,next;
};
node edge[MAXN];
int head[MAXN],cnt,n,m,s;
bool vis[MAXN];
void addEdge(int u,int v){
	edge[++cnt] = (node){v,head[u]};
	head[u] = cnt;
}
void dfs(int u){
	for(int i = head[u]; i; i = edge[i].next){
		int v = edge[i].v;
		if(!vis[v]){
			s ++;
			vis[v] = true;
			dfs(v);
		}
	}
}
int main(){
	int x,y;
	cin >> n >> m;
	for(int i = 1;i<= m; i++){
		cin >> x >> y;
		addEdge(x,y);
	}
	for(int i =1; i<= n; i++){
		s = 1;
		memset(vis,false,sizeof(vis));
		dfs(i);
		cout << s << endl;
	}
	return 0;
}

10分钟写完程序,没有仔细分析数据量,然后就TLE。
那么暴力搜不行了。如何是好?
二、进一步看题,注意一个重要信息,这个图是有向无环图(DAG),如果有一条边(u - > v),从u出发的能够到达的点,是从u所能到达的所有后继节点v出发能到达的点的和,再加上自己本身就是题解。那么我们需要先算出v所能达到的所有点,依此下去,我们发现,需要从图的路径的末端往起点推,如何保证点的枚举顺序是这样的呢?拓扑排序可以满足这一点,将图进行拓扑排序后,倒序后便是这样的特性。
  另外我们如何u-v求所有v的能到达的点呢。我们可以采用一个N位二进制数存储s[x],如果第i位为1表示x能到i点。那么最后只需要看s[x]里存储的二进制里有多少个1既能知道能到多少个点。运用C++STL里的bitset可以帮助我们存储。
  AC代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 30000+6;
struct node{
	int v,next;
};
queue<int>q;
node edge[MAXN];
int a[MAXN],head[MAXN],cnt,cnts,n,sum ,m,rudu[MAXN];
bool vis[MAXN];
bitset<MAXN> s[MAXN];
void addEdge(int u,int v){
	edge[++cnt] = (node){v,head[u]};
	head[u] = cnt;
}
void topSort(){
	for(int i = 1;i <= n; i++){
		if(rudu[i] == 0) q.push(i);
	}
	while(!q.empty()){
		int u = q.front();
		a[++cnts] = u;
		q.pop();
		for(int i = head[u]; i; i= edge[i].next){
			int v = edge[i].v;
			rudu[v] --;
			if(rudu[v] == 0) q.push(v);
		}
	}
}
void calc(){
	for(int i = cnts; i; i--){
		int u = a[i];
		s[u][u] = 1;//将第u位置为1
		for(int j = head[u]; j; j = edge[j].next){
			int v = edge[j].v;
			s[u] = s[u] | s[v];
		} 
	}
}
int main(){
	int x,y;
	cin >> n >> m;
	for(int i = 1;i<= m; i++){
		scanf("%d%d",&x , &y);
		addEdge(x,y);
		rudu[y] ++;
	}	
	topSort();
	calc();
	for(int i = 1;i<= n;i++){
		printf("%d\n" , s[i].count());
	}
	return 0;
}