1. 程式人生 > 實用技巧 >可達性統計

可達性統計

給定一張N個點M條邊的有向無環圖,分別統計從每個點出發能夠到達的點的數量。

輸入格式

第一行兩個整數N,M,接下來M行每行兩個整數x,y,表示從x到y的一條有向邊。

輸出格式

輸出共N行,表示每個點能夠到達的點的數量。

資料範圍

1≤N,M≤30000<?XML:NAMESPACE PREFIX = "[default] http://www.w3.org/1998/Math/MathML" NS = "http://www.w3.org/1998/Math/MathML" />1≤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


首先,要求的是每個點出發能到達的點數包括它本身。注意是有向無環圖。
思路:
1. 由推導可知(一看就知道嘿嘿),假設當前點是 x, 則通過x能到達的點,是由x
所指向的點的指向的點的…..,的集合。
2. 設f(x)為x點能到達的點數,
    f(x) = {x} U {f(son1), f(son2), …};//這裡的son代表x指向的點,f(son)就是兒子能到
達的點數。
3.通過2的公式可以得知,計算應該從尾巴節點開始算,然後一步步累加, 所以我們可以先求出無向圖的
拓撲排序,然後反向的計算,因為拓撲排序反過來就是從尾巴開始的。
4。看資料是30000,所以直接開二維陣列會爆,這個時候就要用到二進位制狀態壓縮,STL庫中有bitset
直接用就ok

程式碼:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <bitset>
#include <queue>
using namespace std;

const  int N = 30010;
int n, m;
int h[N], e[N], ne[N], idx;

int d[N], seq[N];//seq存拓撲排序

bitset<N> f[N];

void add(int a, int b) {//鄰接表存圖
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void
topsort() { queue<int> q; for(int i = 1; i <= n; ++ i){ if(!d[i]) q.push(i);//入度為零的點加入佇列 } int k = 0; while(!q.empty()) { int t = q.front(); q.pop(); seq[k++] = t; for(int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if(-- d[j] == 0)//入度為零的點加入佇列 q.push(j); } } } int main() { cin >> n >> m; memset(h, -1, sizeof h); for(int i = 0; i < m; ++ i) { int a, b; cin >> a >> b; add(a,b); d[b] ++;//計算入度 } topsort(); for(int i = n - 1; ~i; -- i){ int j = seq[i];//j點指向的下一個點 f[j][j] = 1;//到它自己也算一個點 for(int p = h[j]; ~p; p = ne[p]) f[j] |= f[e[p]];//將對應的二進位制位變成1 } for(int i = 1; i <= n; ++ i) cout << f[i].count() << endl;//統計每個點的二進位制位1的個數就是答案 return 0; }