ch 2101 可达性统计 拓扑排序+状压dp
题目:
描述
给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。N,M≤30000。
输入格式
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。
输出格式
共N行,表示每个点能够到达的点的数量。
样例输入
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.首先想到的是从后向前开始更新每一个点的可达值-->拓扑排序提供从后向前更新的可能
2.继而思考如何从后向前更新,设拓扑序(x,y),x在y前,那么,若x可达y,那么y的所有可达点都是x的可达点
3.我们不妨使用状态压缩方法,对一串二进制数ans[x],1表示第i位对第x位来说可达,0表示不可达
4.在3的基础上知道,可以使用 ans[x]=ans[x]|ans[y] 向前更新可达值
5.为了实现方便,引入 birset<size> ans[maxn] 来存储状压数据
AC code:
#include<bits/stdc++.h> using namespace std; bitset<30005> ans[30005]; const int maxn=30000+5; int deg[maxn]; int topo[maxn]; vector<int> dag[maxn]; vector<int>::iterator it; int tot,cnt,n,m; void toposort() { queue<int> que; for(int i=1;i<=n;i++) { if(deg[i]==0) que.push(i); } while(que.size()) { int x=que.front(); que.pop(); topo[++cnt]=x; for(int i=0;i<dag[x].size();i++) { int y=dag[x][i]; if(--deg[y]==0) que.push(y); } } } int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); dag[u].push_back(v); deg[v]++; } toposort(); for(int i=cnt;i>0;--i) { int x=topo[i]; ans[x].reset(); ans[x][x]=1; for(int i=0;i<dag[x].size();i++) { ans[x]|=ans[dag[x][i]]; } } for(int i=1;i<=n;i++) cout<<ans[i].count()<<endl; return 0; }