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

题目传送门: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;
}
发布了88 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/xuechen_gemgirl/article/details/89353229