164. 可达性统计 (拓扑排序 + 状态压缩)
程序员文章站
2024-03-19 10:21:34
...
给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。
输入格式
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。
输出格式
输出共N行,表示每个点能够到达的点的数量。
数据范围
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
一开始以为是个水题,想都没想dfs就敲起来了。
结果TLE,y总说还会爆内存。。。
对于时间复杂度和空间复杂度还是不能很好的估量。
注意开数组变量时,大一点点就行了。小心爆内存!
因为是有向无环图,所以可以拓扑排序,拓扑排序完之后有一个很好的性质,就是所有的有向边都是从前指向后。
设f[x]为x点所能到达的点的集合,集合关系的表示可以用位运算来表示,bitset很好用。
topsort+ 状态压缩
AC Code:
#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define maxn (LL)1e5
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define PI acos(-1.0)
#define pb push_back
#define re register
const double eps = 0.0000001;
using namespace std;
typedef pair<LL,LL> pii;
inline LL sgn(double x) {
return (x > eps) - (x < -eps);
}
const int N = 30010;
using namespace std;
struct Edge
{
int next;
int to;
}edge[N];
int head[N],tot;
int deg[N];
int ans[N];
int n,m;bitset<N> f[N];
int k = 1;
inline void add(int from,int to)
{
edge[++tot].next = head[from];
edge[tot].to = to;
deg[to]++;
head[from] = tot;
}
void topsort()//拓扑排序
{
queue<int> Q;
for(int i = 1;i<=n;++i)
{
if(deg[i]==0) Q.push(i);
}
while(Q.size())
{
int x = Q.front();
ans[k++] = x;
Q.pop();
for(int i = head[x];~i;i = edge[i].next)
{
int y = edge[i].to;
if(--deg[y]==0) Q.push(y);
}
}
}
int main()
{
//freopen("input.txt","r",stdin);
memset(head,-1,sizeof(head));
cin>>n>>m;
memset(head,-1,sizeof(head));
for(int i = 1;i<=m;++i)
{
int f,t;
cin>>f>>t;
add(f,t);
}
topsort();
for(int i = k-1;i>=1;i--)//倒着推集合关系
{
int x = ans[i];
f[x][x] = 1;
for(int j = head[x];~j;j = edge[j].next)
{
int y = edge[j].to;
f[x] |= f[y];
}
}
for(int i = 1;i<=n;++i)
{
cout<<f[i].count()<<endl;
}
}