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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章